aiobungie

A Pythonic async/await wrapper for interacting with the Bungie API.

Base client.

Example
import aiobungie

client = aiobungie.Client('YOUR_API_KEY')

# Search for Destiny2 users.
async def main() -> None:
    async with client.rest:
        users = await client.search_users('Crit')

        # Iterate over the users and take the first 5 results.
        for user in users.take(5):
            print(f'{user.name} ({user.code})')

            # Iterate through the users memberships.
            for membership in user.memberships:
                print(membership.type, membership.id)

client.run(main()) # or asyncio.run(main())

Single RESTClient instance.

The difference between base client and the REST clients:

  • No Hight-Level concepts.
  • All returned data are pure JSON objects from the API.
  • No object creation.
Example
import aiobungie

async def main() -> None:
    # Using `async with` context manager to close the session properly.
    async with aiobungie.RESTClient("TOKEN") as rest:
        payload = await rest.fetch_player('Fate怒', 4275)

        for membership in payload:
            print(membership['membershipId'], membership['iconPath'])

import asyncio
asyncio.run(main())

REST client pool.

A REST client pool allows you to acquire multiple RESTClient instances that shares the same connection.

Example
import aiobungie
import asyncio

pool = aiobungie.RESTPool("token")

async def func1() -> None:
    async with pool.acquire() as instance:
        tokens = await instance.fetch_oauth2_tokens('code')
        pool.metadata['tokens'] = tokens

# Other instance may access the tokens from pool since its shared.

async def func2() -> None:
    async with pool.acquire() as instance:
        tokens = pool.metadata['tokens']
        tokens = await instance.refresh_access_token(tokens.refresh_token)

async def main() -> None:
    await asyncio.gather(func1(), func2())

asyncio.run(main())

Should you use the base client or the REST client? This returns to you. For an example if you're building a website.

You can use python as a REST API in the backend with the RESTClient since all returned object are JSON objects. Which gives you the freedom to deserialize it and implement your own logic in the front-end.

Or of you're building a Discord bot for an example or something simple. The base client is the way to go.

  1# MIT License
  2#
  3# Copyright (c) 2020 - Present nxtlo
  4#
  5# Permission is hereby granted, free of charge, to any person obtaining a copy
  6# of this software and associated documentation files (the "Software"), to deal
  7# in the Software without restriction, including without limitation the rights
  8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9# copies of the Software, and to permit persons to whom the Software is
 10# furnished to do so, subject to the following conditions:
 11#
 12# The above copyright notice and this permission notice shall be included in all
 13# copies or substantial portions of the Software.
 14#
 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21# SOFTWARE.
 22
 23"""A Pythonic `async`/`await` wrapper for interacting with the Bungie API.
 24
 25Base client.
 26
 27Example
 28-------
 29```py
 30import aiobungie
 31
 32client = aiobungie.Client('YOUR_API_KEY')
 33
 34# Search for Destiny2 users.
 35async def main() -> None:
 36    async with client.rest:
 37        users = await client.search_users('Crit')
 38
 39        # Iterate over the users and take the first 5 results.
 40        for user in users.take(5):
 41            print(f'{user.name} ({user.code})')
 42
 43            # Iterate through the users memberships.
 44            for membership in user.memberships:
 45                print(membership.type, membership.id)
 46
 47client.run(main()) # or asyncio.run(main())
 48```
 49
 50Single RESTClient instance.
 51
 52The difference between base client and the REST clients:
 53
 54* No Hight-Level concepts.
 55* All returned data are pure JSON objects from the API.
 56* No object creation.
 57
 58Example
 59-------
 60```py
 61import aiobungie
 62
 63async def main() -> None:
 64    # Using `async with` context manager to close the session properly.
 65    async with aiobungie.RESTClient("TOKEN") as rest:
 66        payload = await rest.fetch_player('Fate怒', 4275)
 67
 68        for membership in payload:
 69            print(membership['membershipId'], membership['iconPath'])
 70
 71import asyncio
 72asyncio.run(main())
 73```
 74
 75REST client pool.
 76
 77A REST client pool allows you to acquire multiple `RESTClient` instances that shares the same connection.
 78
 79Example
 80-------
 81```py
 82import aiobungie
 83import asyncio
 84
 85pool = aiobungie.RESTPool("token")
 86
 87async def func1() -> None:
 88    async with pool.acquire() as instance:
 89        tokens = await instance.fetch_oauth2_tokens('code')
 90        pool.metadata['tokens'] = tokens
 91
 92# Other instance may access the tokens from pool since its shared.
 93
 94async def func2() -> None:
 95    async with pool.acquire() as instance:
 96        tokens = pool.metadata['tokens']
 97        tokens = await instance.refresh_access_token(tokens.refresh_token)
 98
 99async def main() -> None:
100    await asyncio.gather(func1(), func2())
101
102asyncio.run(main())
103```
104
105Should you use the base client or the REST client?
106This returns to you. For an example if you're building a website.
107
108You can use python as a REST API in the backend with the RESTClient since all returned object are JSON objects.
109Which gives you the freedom to deserialize it and implement your own logic in the front-end.
110
111Or of you're building a Discord bot for an example or something simple. The base client is the way to go.
112"""
113
114
115from __future__ import annotations
116
117from aiobungie import builders
118from aiobungie import crates
119from aiobungie import interfaces
120from aiobungie import traits
121from aiobungie import typedefs
122from aiobungie import url
123from aiobungie.client import Client
124from aiobungie.error import *
125from aiobungie.internal import iterators
126from aiobungie.internal.assets import Image
127from aiobungie.internal.enums import *
128from aiobungie.internal.factory import Factory
129from aiobungie.internal.iterators import *
130from aiobungie.rest import *
131from aiobungie.undefined import UNDEFINED
132from aiobungie.undefined import UndefinedOr
133from aiobungie.undefined import UndefinedType
134
135from .metadata import __about__
136from .metadata import __author__
137from .metadata import __docs__
138from .metadata import __email__
139from .metadata import __license__
140from .metadata import __url__
141from .metadata import __version__
142
143# Alias for crate for backwards compatibility.
144crate = crates
145
146# Activity enums
147from .crates.activity import Difficulty
148
149# Components enums
150from .crates.components import ComponentFields
151from .crates.components import ComponentPrivacy
152
153# Entity enums
154from .crates.entity import GatingScope
155from .crates.entity import ObjectiveUIStyle
156from .crates.entity import ValueUIStyle
157
158# Fireteam enums.
159from .crates.fireteams import FireteamActivity
160from .crates.fireteams import FireteamDate
161from .crates.fireteams import FireteamLanguage
162from .crates.fireteams import FireteamPlatform
163
164# Records enums
165from .crates.records import RecordState
166
167__all__ = [mod for mod in dir() if not mod.startswith("_")]  # type: ignore
@attrs.define(auto_exc=True)
class AiobungieError(builtins.RuntimeError):
74@attrs.define(auto_exc=True)
75class AiobungieError(RuntimeError):
76    """Base class that all other exceptions inherit from."""

Base class that all other exceptions inherit from.

AiobungieError()
2def __init__(self, ):
3    BaseException.__init__(self, )

Method generated by attrs for class AiobungieError.

Inherited Members
builtins.BaseException
with_traceback
@typing.final
class AmmoType(builtins.int, aiobungie.Enum):
643@typing.final
644class AmmoType(int, Enum):
645    """AN enum for Detyiny 2 ammo types."""
646
647    NONE = 0
648    PRIMARY = 1
649    SPECIAL = 2
650    HEAVY = 3

AN enum for Detyiny 2 ammo types.

NONE = <AmmoType.NONE: 0>
PRIMARY = <AmmoType.PRIMARY: 1>
SPECIAL = <AmmoType.SPECIAL: 2>
HEAVY = <AmmoType.HEAVY: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class BadRequest(aiobungie.HTTPError):
164@attrs.define(auto_exc=True)
165class BadRequest(HTTPError):
166    """An exception raised when requesting a resource with the provided data is wrong."""
167
168    url: typing.Optional[typedefs.StrOrURL]
169    """The URL/endpoint caused this error."""
170
171    body: typing.Any
172    """The response body."""
173
174    headers: multidict.CIMultiDictProxy[str]
175    """The response headers."""
176
177    http_status: http.HTTPStatus = attrs.field(
178        default=http.HTTPStatus.BAD_REQUEST, init=False
179    )

An exception raised when requesting a resource with the provided data is wrong.

BadRequest( message: str, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str])
2def __init__(self, message, url, body, headers):
3    self.message = message
4    self.url = url
5    self.body = body
6    self.headers = headers
7    self.http_status = attr_dict['http_status'].default
8    BaseException.__init__(self, self.message,self.url,self.body,self.headers)

Method generated by attrs for class BadRequest.

url: Union[str, yarl.URL, NoneType]

The URL/endpoint caused this error.

body: Any

The response body.

headers: multidict._multidict.CIMultiDictProxy[str]

The response headers.

http_status: http.HTTPStatus

The response status.

Inherited Members
HTTPError
message
builtins.BaseException
with_traceback
@typing.final
class ClanMemberType(builtins.int, aiobungie.Enum):
698@typing.final
699class ClanMemberType(int, Enum):
700    """An enum for bungie clan member types."""
701
702    NONE = 0
703    BEGINNER = 1
704    MEMBER = 2
705    ADMIN = 3
706    ACTING_FOUNDER = 4
707    FOUNDER = 5

An enum for bungie clan member types.

NONE = <ClanMemberType.NONE: 0>
BEGINNER = <ClanMemberType.BEGINNER: 1>
MEMBER = <ClanMemberType.MEMBER: 2>
ADMIN = <ClanMemberType.ADMIN: 3>
ACTING_FOUNDER = <ClanMemberType.ACTING_FOUNDER: 4>
FOUNDER = <ClanMemberType.FOUNDER: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Class(builtins.int, aiobungie.Enum):
474@typing.final
475class Class(int, Enum):
476    """An Enum for Destiny character classes."""
477
478    TITAN = 0
479    HUNTER = 1
480    WARLOCK = 2
481    UNKNOWN = 3

An Enum for Destiny character classes.

TITAN = <Class.TITAN: 0>
HUNTER = <Class.HUNTER: 1>
WARLOCK = <Class.WARLOCK: 2>
UNKNOWN = <Class.UNKNOWN: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Client(aiobungie.traits.ClientApp):
  60class Client(traits.ClientApp):
  61    """Standard Bungie API client application.
  62
  63    This client deserialize the REST JSON responses using `aiobungie.internal.factory.Factory`
  64    and returns `aiobungie.crates` Python object implementations of the responses.
  65
  66    A `aiobungie.RESTClient` REST client can also be used alone for low-level concepts.
  67
  68    Example
  69    -------
  70    ```py
  71    import aiobungie
  72
  73    client = aiobungie.Client('...')
  74
  75    async def main():
  76        async with client.rest:
  77            user = await client.fetch_current_user_memberships('...')
  78            print(user)
  79    ```
  80
  81    Parameters
  82    -----------
  83    token: `str`
  84        Your Bungie's API key or Token from the developer's portal.
  85
  86    Other Parameters
  87    ----------------
  88    max_retries : `int`
  89        The max retries number to retry if the request hit a `5xx` status code.
  90    client_secret : `str | None`
  91        An optional application client secret,
  92        This is only needed if you're fetching OAuth2 tokens with this client.
  93    client_id : `int | None`
  94        An optional application client id,
  95        This is only needed if you're fetching OAuth2 tokens with this client.
  96    """
  97
  98    __slots__ = ("_rest", "_factory")
  99
 100    def __init__(
 101        self,
 102        token: str,
 103        /,
 104        client_secret: typing.Optional[str] = None,
 105        client_id: typing.Optional[int] = None,
 106        *,
 107        max_retries: int = 4,
 108    ) -> None:
 109
 110        self._rest = rest_.RESTClient(
 111            token,
 112            client_secret,
 113            client_id,
 114            max_retries=max_retries,
 115        )
 116
 117        self._factory = factory_.Factory(self)
 118
 119    @property
 120    def factory(self) -> factory_.Factory:
 121        return self._factory
 122
 123    @property
 124    def rest(self) -> interfaces.RESTInterface:
 125        return self._rest
 126
 127    @property
 128    def request(self) -> Client:
 129        return self
 130
 131    @property
 132    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
 133        return self._rest.metadata
 134
 135    def run(
 136        self, future: collections.Coroutine[typing.Any, None, None], debug: bool = False
 137    ) -> None:
 138        loop: typing.Final[asyncio.AbstractEventLoop] = helpers.get_or_make_loop()
 139        try:
 140            if not loop.is_running():
 141                loop.set_debug(debug)
 142                loop.run_until_complete(future)
 143
 144        except Exception as exc:
 145            raise RuntimeError(f"Failed to run {future.__qualname__}") from exc
 146
 147        except KeyboardInterrupt:
 148            _LOG.warn("Unexpected Keyboard interrupt. Exiting.")
 149            return
 150
 151    # * User methods.
 152
 153    async def fetch_current_user_memberships(self, access_token: str, /) -> user.User:
 154        """Fetch and return a user object of the bungie net user associated with account.
 155
 156        .. warning::
 157            This method requires OAuth2 scope and a Bearer access token.
 158
 159        Parameters
 160        ----------
 161        access_token : `str`
 162            A valid Bearer access token for the authorization.
 163
 164        Returns
 165        -------
 166        `aiobungie.crates.user.User`
 167            A user object includes the Destiny memberships and Bungie.net user.
 168        """
 169        resp = await self.rest.fetch_current_user_memberships(access_token)
 170
 171        return self.factory.deserialize_user(resp)
 172
 173    async def fetch_bungie_user(self, id: int, /) -> user.BungieUser:
 174        """Fetch a Bungie user by their BungieNet id.
 175
 176        .. note::
 177            This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id`
 178            for other memberships.
 179
 180        Parameters
 181        ----------
 182        id: `int`
 183            The user id.
 184
 185        Returns
 186        -------
 187        `aiobungie.crates.user.BungieUser`
 188            A Bungie user.
 189
 190        Raises
 191        ------
 192        `aiobungie.error.NotFound`
 193            The user was not found.
 194        """
 195        payload = await self.rest.fetch_bungie_user(id)
 196
 197        return self.factory.deserialize_bungie_user(payload)
 198
 199    async def search_users(
 200        self, name: str, /
 201    ) -> iterators.Iterator[user.SearchableDestinyUser]:
 202        """Search for players and return all players that matches the same name.
 203
 204        Parameters
 205        ----------
 206        name : `buildins.str`
 207            The user name.
 208
 209        Returns
 210        -------
 211        `aiobungie.iterators.Iterator[aiobungie.crates.DestinyMembership]`
 212            A sequence of destiny memberships.
 213        """
 214        payload = await self.rest.search_users(name)
 215
 216        return iterators.Iterator(
 217            [
 218                self.factory.deserialize_searched_user(user)
 219                for user in payload["searchResults"]
 220            ]
 221        )
 222
 223    async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]:
 224        """Fetch all available user themes.
 225
 226        Returns
 227        -------
 228        `collections.Sequence[aiobungie.crates.user.UserThemes]`
 229            A sequence of user themes.
 230        """
 231        data = await self.rest.fetch_user_themes()
 232
 233        return self.factory.deserialize_user_themes(data)
 234
 235    async def fetch_hard_types(
 236        self,
 237        credential: int,
 238        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
 239        /,
 240    ) -> user.HardLinkedMembership:
 241        """Gets any hard linked membership given a credential.
 242        Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now.
 243        Cross Save aware.
 244
 245        Parameters
 246        ----------
 247        credential: `int`
 248            A valid SteamID64
 249        type: `aiobungie.CredentialType`
 250            The credential type. This must not be changed
 251            Since its only credential that works "currently"
 252
 253        Returns
 254        -------
 255        `aiobungie.crates.user.HardLinkedMembership`
 256            Information about the hard linked data.
 257        """
 258
 259        payload = await self.rest.fetch_hardlinked_credentials(credential, type)
 260
 261        return user.HardLinkedMembership(
 262            id=int(payload["membershipId"]),
 263            type=enums.MembershipType(payload["membershipType"]),
 264            cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]),
 265        )
 266
 267    async def fetch_membership_from_id(
 268        self,
 269        id: int,
 270        /,
 271        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 272    ) -> user.User:
 273        """Fetch Bungie user's memberships from their id.
 274
 275        Notes
 276        -----
 277        * This returns both BungieNet membership and a sequence of the player's DestinyMemberships
 278        Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
 279        see `aiobungie.crates.user.DestinyMembership` for more details.
 280        * If you only want the bungie user. Consider using `Client.fetch_user` method.
 281
 282        Parameters
 283        ----------
 284        id : `int`
 285            The user's id.
 286        type : `aiobungie.MembershipType`
 287            The user's membership type.
 288
 289        Returns
 290        -------
 291        `aiobungie.crates.User`
 292            A Bungie user with their membership types.
 293
 294        Raises
 295        ------
 296        aiobungie.NotFound
 297            The requested user was not found.
 298        """
 299        payload = await self.rest.fetch_membership_from_id(id, type)
 300
 301        return self.factory.deserialize_user(payload)
 302
 303    async def fetch_user_credentials(
 304        self, access_token: str, membership_id: int, /
 305    ) -> collections.Sequence[user.UserCredentials]:
 306        """Fetch an array of credential types attached to the requested account.
 307
 308        .. note::
 309            This method require OAuth2 Bearer access token.
 310
 311        Parameters
 312        ----------
 313        access_token : `str`
 314            The bearer access token associated with the bungie account.
 315        membership_id : `int`
 316            The id of the membership to return.
 317
 318        Returns
 319        -------
 320        `collections.Sequence[aiobungie.crates.UserCredentials]`
 321            A sequence of the attached user credentials.
 322
 323        Raises
 324        ------
 325        `aiobungie.Unauthorized`
 326            The access token was wrong or no access token passed.
 327        """
 328        resp = await self.rest.fetch_user_credentials(access_token, membership_id)
 329
 330        return self.factory.deserialize_user_credentials(resp)
 331
 332    # * Destiny 2.
 333
 334    async def fetch_profile(
 335        self,
 336        member_id: int,
 337        type: typedefs.IntAnd[enums.MembershipType],
 338        components: list[enums.ComponentType],
 339        auth: typing.Optional[str] = None,
 340    ) -> components.Component:
 341        """
 342        Fetch a bungie profile passing components to the request.
 343
 344        Parameters
 345        ----------
 346        member_id: `int`
 347            The member's id.
 348        type: `aiobungie.MembershipType`
 349            A valid membership type.
 350        components : `list[aiobungie.ComponentType]`
 351            List of profile components to collect and return.
 352
 353        Other Parameters
 354        ----------------
 355        auth : `typing.Optional[str]`
 356            A Bearer access_token to make the request with.
 357            This is optional and limited to components that only requires an Authorization token.
 358
 359        Returns
 360        --------
 361        `aiobungie.crates.Component`
 362            A Destiny 2 player profile with its components.
 363            Only passed components will be available if they exists. Otherwise they will be `None`
 364
 365        Raises
 366        ------
 367        `aiobungie.MembershipTypeError`
 368            The provided membership type was invalid.
 369        """
 370        data = await self.rest.fetch_profile(member_id, type, components, auth)
 371        return self.factory.deserialize_components(data)
 372
 373    async def fetch_linked_profiles(
 374        self,
 375        member_id: int,
 376        member_type: typedefs.IntAnd[enums.MembershipType],
 377        /,
 378        *,
 379        all: bool = False,
 380    ) -> profile.LinkedProfile:
 381        """Returns a summary information about all profiles linked to the requested member.
 382
 383        The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
 384
 385        .. note::
 386            It will only return linked accounts whose linkages you are allowed to view.
 387
 388        Parameters
 389        ----------
 390        member_id : `int`
 391            The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
 392        member_type : `aiobungie.MembershipType`
 393            The type for the membership whose linked Destiny account you want to return.
 394
 395        Other Parameters
 396        ----------------
 397        all : `bool`
 398            If provided and set to `True`, All memberships regardless
 399            of whether they're obscured by overrides will be returned,
 400
 401            If provided and set to `False`, Only available memberships will be returned.
 402            The default for this is `False`.
 403
 404        Returns
 405        -------
 406        `aiobungie.crates.profile.LinkedProfile`
 407            A linked profile object.
 408        """
 409        resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all)
 410
 411        return self.factory.deserialize_linked_profiles(resp)
 412
 413    async def fetch_player(
 414        self,
 415        name: str,
 416        code: int,
 417        /,
 418        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
 419    ) -> collections.Sequence[user.DestinyMembership]:
 420        """Fetch a Destiny 2 player's memberships.
 421
 422        Parameters
 423        -----------
 424        name: `str`
 425            The unique Bungie player name.
 426        code : `int`
 427            The unique Bungie display name code.
 428        type: `aiobungie.internal.enums.MembershipType`
 429            The player's membership type, e,g. XBOX, STEAM, PSN
 430
 431        Returns
 432        --------
 433        `collections.Sequence[aiobungie.crates.DestinyMembership]`
 434            A sequence of the found Destiny 2 player memberships.
 435            An empty sequence will be returned if no one found.
 436
 437        Raises
 438        ------
 439        `aiobungie.MembershipTypeError`
 440            The provided membership type was invalid.
 441        """
 442        resp = await self.rest.fetch_player(name, code, type)
 443
 444        return self.factory.deserialize_destiny_memberships(resp)
 445
 446    async def fetch_character(
 447        self,
 448        member_id: int,
 449        membership_type: typedefs.IntAnd[enums.MembershipType],
 450        character_id: int,
 451        components: list[enums.ComponentType],
 452        auth: typing.Optional[str] = None,
 453    ) -> components.CharacterComponent:
 454        """Fetch a Destiny 2 character.
 455
 456        Parameters
 457        ----------
 458        member_id: `int`
 459            A valid bungie member id.
 460        character_id: `int`
 461            The Destiny character id to retrieve.
 462        membership_type: `aiobungie.internal.enums.MembershipType`
 463            The member's membership type.
 464        components: `list[aiobungie.ComponentType]`
 465            Multiple arguments of character components to collect and return.
 466
 467        Other Parameters
 468        ----------------
 469        auth : `typing.Optional[str]`
 470            A Bearer access_token to make the request with.
 471            This is optional and limited to components that only requires an Authorization token.
 472
 473        Returns
 474        -------
 475        `aiobungie.crates.CharacterComponent`
 476            A Bungie character component.
 477
 478        `aiobungie.MembershipTypeError`
 479            The provided membership type was invalid.
 480        """
 481        resp = await self.rest.fetch_character(
 482            member_id, membership_type, character_id, components, auth
 483        )
 484
 485        return self.factory.deserialize_character_component(resp)
 486
 487    async def fetch_unique_weapon_history(
 488        self,
 489        membership_id: int,
 490        character_id: int,
 491        membership_type: typedefs.IntAnd[enums.MembershipType],
 492    ) -> collections.Sequence[activity.ExtendedWeaponValues]:
 493        """Fetch details about unique weapon usage for a character. Includes all exotics.
 494
 495        Parameters
 496        ----------
 497        membership_id : `int`
 498            The Destiny user membership id.
 499        character_id : `int`
 500            The character id to retrieve.
 501        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 502            The Destiny user's membership type.
 503
 504        Returns
 505        -------
 506        `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]`
 507            A sequence of the weapon's extended values.
 508        """
 509        resp = await self._rest.fetch_unique_weapon_history(
 510            membership_id, character_id, membership_type
 511        )
 512
 513        return [
 514            self._factory.deserialize_extended_weapon_values(weapon)
 515            for weapon in resp["weapons"]
 516        ]
 517
 518    # * Destiny 2 Activities.
 519
 520    async def fetch_activities(
 521        self,
 522        member_id: int,
 523        character_id: int,
 524        mode: typedefs.IntAnd[enums.GameMode],
 525        *,
 526        membership_type: typedefs.IntAnd[
 527            enums.MembershipType
 528        ] = enums.MembershipType.ALL,
 529        page: int = 0,
 530        limit: int = 250,
 531    ) -> iterators.Iterator[activity.Activity]:
 532        """Fetch a Destiny 2 activity for the specified character id.
 533
 534        Parameters
 535        ----------
 536        member_id: `int`
 537            The user id that starts with `4611`.
 538        character_id: `int`
 539            The id of the character to retrieve the activities for.
 540        mode: `aiobungie.typedefs.IntAnd[aiobungie.internal.enums.GameMode]`
 541            This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
 542
 543        Other Parameters
 544        ----------------
 545        membership_type: `aiobungie.internal.enums.MembershipType`
 546            The Member ship type, if nothing was passed than it will return all.
 547        page: int
 548            The page number. Default is `0`
 549        limit: int
 550            Limit the returned result. Default is `250`.
 551
 552        Returns
 553        -------
 554        `aiobungie.iterators.Iterator[aiobungie.crates.Activity]`
 555            An iterator of the player's activities.
 556
 557        Raises
 558        ------
 559        `aiobungie.MembershipTypeError`
 560            The provided membership type was invalid.
 561        """
 562        resp = await self.rest.fetch_activities(
 563            member_id,
 564            character_id,
 565            mode,
 566            membership_type=membership_type,
 567            page=page,
 568            limit=limit,
 569        )
 570
 571        return self.factory.deserialize_activities(resp)
 572
 573    async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity:
 574        """Fetch a post activity details.
 575
 576        Parameters
 577        ----------
 578        instance_id: `int`
 579            The activity instance id.
 580
 581        Returns
 582        -------
 583        `aiobungie.crates.PostActivity`
 584           A post activity object.
 585        """
 586        resp = await self.rest.fetch_post_activity(instance_id)
 587
 588        return self.factory.deserialize_post_activity(resp)
 589
 590    async def fetch_aggregated_activity_stats(
 591        self,
 592        character_id: int,
 593        membership_id: int,
 594        membership_type: typedefs.IntAnd[enums.MembershipType],
 595    ) -> iterators.Iterator[activity.AggregatedActivity]:
 596        """Fetch aggregated activity stats for a character.
 597
 598        Parameters
 599        ----------
 600        character_id: `int`
 601            The id of the character to retrieve the activities for.
 602        membership_id: `int`
 603            The id of the user that started with `4611`.
 604        membership_type: `aiobungie.internal.enums.MembershipType`
 605            The Member ship type.
 606
 607        Returns
 608        -------
 609        `aiobungie.iterators.Iterator[aiobungie.crates.AggregatedActivity]`
 610            An iterator of the player's activities.
 611
 612        Raises
 613        ------
 614        `aiobungie.MembershipTypeError`
 615            The provided membership type was invalid.
 616        """
 617        resp = await self.rest.fetch_aggregated_activity_stats(
 618            character_id, membership_id, membership_type
 619        )
 620
 621        return self.factory.deserialize_aggregated_activities(resp)
 622
 623    # * Destiny 2 Clans or GroupsV2.
 624
 625    async def fetch_clan_from_id(
 626        self,
 627        id: int,
 628        /,
 629        access_token: typing.Optional[str] = None,
 630    ) -> clans.Clan:
 631        """Fetch a Bungie Clan by its id.
 632
 633        Parameters
 634        -----------
 635        id: `int`
 636            The clan id.
 637
 638        Returns
 639        --------
 640        `aiobungie.crates.Clan`
 641            An Bungie clan.
 642
 643        Raises
 644        ------
 645        `aiobungie.NotFound`
 646            The clan was not found.
 647        """
 648        resp = await self.rest.fetch_clan_from_id(id, access_token)
 649
 650        return self.factory.deserialize_clan(resp)
 651
 652    async def fetch_clan(
 653        self,
 654        name: str,
 655        /,
 656        access_token: typing.Optional[str] = None,
 657        *,
 658        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 659    ) -> clans.Clan:
 660        """Fetch a Clan by its name.
 661        This method will return the first clan found with given name.
 662
 663        Parameters
 664        ----------
 665        name: `str`
 666            The clan name
 667
 668        Other Parameters
 669        ----------------
 670        access_token : `typing.Optional[str]`
 671            An optional access token to make the request with.
 672
 673            If the token was bound to a member of the clan,
 674            This field `aiobungie.crates.Clan.current_user_membership` will be available
 675            and will return the membership of the user who made this request.
 676        type : `aiobungie.GroupType`
 677            The group type, Default is aiobungie.GroupType.CLAN.
 678
 679        Returns
 680        -------
 681        `aiobungie.crates.Clan`
 682            A Bungie clan.
 683
 684        Raises
 685        ------
 686        `aiobungie.NotFound`
 687            The clan was not found.
 688        """
 689        resp = await self.rest.fetch_clan(name, access_token, type=type)
 690
 691        return self.factory.deserialize_clan(resp)
 692
 693    async def fetch_clan_conversations(
 694        self, clan_id: int, /
 695    ) -> collections.Sequence[clans.ClanConversation]:
 696        """Fetch the conversations/chat channels of the given clan id.
 697
 698        Parameters
 699        ----------
 700        clan_id : `int`
 701            The clan id.
 702
 703        Returns
 704        `collections.Sequence[aiobungie.crates.ClanConversation]`
 705            A sequence of the clan chat channels.
 706        """
 707        resp = await self.rest.fetch_clan_conversations(clan_id)
 708
 709        return self.factory.deserialize_clan_conversations(resp)
 710
 711    async def fetch_clan_admins(
 712        self, clan_id: int, /
 713    ) -> iterators.Iterator[clans.ClanMember]:
 714        """Fetch the clan founder and admins.
 715
 716        Parameters
 717        ----------
 718        clan_id : `int`
 719            The clan id.
 720
 721        Returns
 722        -------
 723        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
 724            An iterator over the found clan admins and founder.
 725
 726        Raises
 727        ------
 728        `aiobungie.NotFound`
 729            The requested clan was not found.
 730        """
 731        resp = await self.rest.fetch_clan_admins(clan_id)
 732
 733        return self.factory.deserialize_clan_members(resp)
 734
 735    async def fetch_groups_for_member(
 736        self,
 737        member_id: int,
 738        member_type: typedefs.IntAnd[enums.MembershipType],
 739        /,
 740        *,
 741        filter: int = 0,
 742        group_type: enums.GroupType = enums.GroupType.CLAN,
 743    ) -> collections.Sequence[clans.GroupMember]:
 744        """Fetch information about the groups that a given member has joined.
 745
 746        Parameters
 747        ----------
 748        member_id : `int`
 749            The member's id
 750        member_type : `aiobungie.MembershipType`
 751            The member's membership type.
 752
 753        Other Parameters
 754        ----------------
 755        filter : `int`
 756            Filter apply to list of joined groups. This Default to `0`
 757        group_type : `aiobungie.GroupType`
 758            The group's type.
 759            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
 760
 761        Returns
 762        -------
 763        `collections.Sequence[aiobungie.crates.GroupMember]`
 764            A sequence of joined groups for the fetched member.
 765        """
 766        resp = await self.rest.fetch_groups_for_member(
 767            member_id, member_type, filter=filter, group_type=group_type
 768        )
 769
 770        return [
 771            self.factory.deserialize_group_member(group) for group in resp["results"]
 772        ]
 773
 774    async def fetch_potential_groups_for_member(
 775        self,
 776        member_id: int,
 777        member_type: typedefs.IntAnd[enums.MembershipType],
 778        /,
 779        *,
 780        filter: int = 0,
 781        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 782    ) -> collections.Sequence[clans.GroupMember]:
 783        """Fetch the potential groups for a clan member.
 784
 785        Parameters
 786        ----------
 787        member_id : `int`
 788            The member's id
 789        member_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 790            The member's membership type.
 791
 792        Other Parameters
 793        ----------------
 794        filter : `int`
 795            Filter apply to list of joined groups. This Default to `0`
 796        group_type : `aiobungie.typedefs.IntAnd[aiobungie.GroupType]`
 797            The group's type.
 798            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
 799
 800        Returns
 801        -------
 802        `collections.Sequence[aiobungie.crates.GroupMember]`
 803            A sequence of joined potential groups for the fetched member.
 804        """
 805        resp = await self.rest.fetch_potential_groups_for_member(
 806            member_id, member_type, filter=filter, group_type=group_type
 807        )
 808
 809        return [
 810            self.factory.deserialize_group_member(group) for group in resp["results"]
 811        ]
 812
 813    async def fetch_clan_members(
 814        self,
 815        clan_id: int,
 816        /,
 817        *,
 818        name: typing.Optional[str] = None,
 819        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 820    ) -> iterators.Iterator[clans.ClanMember]:
 821        """Fetch Bungie clan members.
 822
 823        Parameters
 824        ----------
 825        clan_id : `int`
 826            The clans id
 827
 828        Other Parameters
 829        ----------------
 830        name : `typing.Optional[str]`
 831            If provided, Only players matching this name will be returned.
 832        type : `aiobungie.MembershipType`
 833            An optional clan member's membership type.
 834            This parameter is used to filter the returned results
 835            by the provided membership, For an example XBox memberships only,
 836            Otherwise will return all memberships.
 837
 838        Returns
 839        -------
 840        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
 841            An iterator over the bungie clan members.
 842
 843        Raises
 844        ------
 845        `aiobungie.NotFound`
 846            The clan was not found.
 847        """
 848        resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name)
 849
 850        return self.factory.deserialize_clan_members(resp)
 851
 852    async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]:
 853        """Fetch the clan banners.
 854
 855        Returns
 856        -------
 857        `collections.Sequence[aiobungie.crates.ClanBanner]`
 858            A sequence of the clan banners.
 859        """
 860        resp = await self.rest.fetch_clan_banners()
 861
 862        return self.factory.deserialize_clan_banners(resp)
 863
 864    # This method is required to be here since it deserialize the clan.
 865    async def kick_clan_member(
 866        self,
 867        access_token: str,
 868        /,
 869        group_id: int,
 870        membership_id: int,
 871        membership_type: typedefs.IntAnd[enums.MembershipType],
 872    ) -> clans.Clan:
 873        """Kick a member from the clan.
 874
 875        .. note::
 876            This request requires OAuth2: oauth2: `AdminGroups` scope.
 877
 878        Parameters
 879        ----------
 880        access_token : `str`
 881            The bearer access token associated with the bungie account.
 882        group_id: `int`
 883            The group id.
 884        membership_id : `int`
 885            The member id to kick.
 886        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
 887            The member's membership type.
 888
 889        Returns
 890        -------
 891        `aiobungie.crates.clan.Clan`
 892            The clan that the member was kicked from.
 893        """
 894        resp = await self.rest.kick_clan_member(
 895            access_token,
 896            group_id=group_id,
 897            membership_id=membership_id,
 898            membership_type=membership_type,
 899        )
 900
 901        return self.factory.deserialize_clan(resp)
 902
 903    async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone:
 904        """Fetch a Bungie clan's weekly reward state.
 905
 906        Parameters
 907        ----------
 908        clan_id : `int`
 909            The clan's id.
 910
 911        Returns
 912        -------
 913        `aiobungie.crates.Milestone`
 914            A runtime status of the clan's milestone data.
 915        """
 916
 917        resp = await self.rest.fetch_clan_weekly_rewards(clan_id)
 918
 919        return self.factory.deserialize_milestone(resp)
 920
 921    # * Destiny 2 Entities aka Definitions.
 922
 923    async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity:
 924        """Fetch a static inventory item entity given a its hash.
 925
 926        Parameters
 927        ----------
 928        hash: `int`
 929            Inventory item's hash.
 930
 931        Returns
 932        -------
 933        `aiobungie.crates.InventoryEntity`
 934            A bungie inventory item.
 935        """
 936        resp = await self.rest.fetch_inventory_item(hash)
 937
 938        return self.factory.deserialize_inventory_entity(resp)
 939
 940    async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity:
 941        """Fetch a Destiny objective entity given a its hash.
 942
 943        Parameters
 944        ----------
 945        hash: `int`
 946            objective's hash.
 947
 948        Returns
 949        -------
 950        `aiobungie.crates.ObjectiveEntity`
 951            An objective entity item.
 952        """
 953        resp = await self.rest.fetch_objective_entity(hash)
 954
 955        return self.factory.deserialize_objective_entity(resp)
 956
 957    async def search_entities(
 958        self, name: str, entity_type: str, *, page: int = 0
 959    ) -> iterators.Iterator[entity.SearchableEntity]:
 960        """Search for Destiny2 entities given a name and its type.
 961
 962        Parameters
 963        ----------
 964        name : `str`
 965            The name of the entity, i.e., Thunderlord, One thousand voices.
 966        entity_type : `str`
 967            The type of the entity, AKA Definition,
 968            For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items.
 969
 970        Other Parameters
 971        ----------------
 972        page : `int`
 973            An optional page to return. Default to 0.
 974
 975        Returns
 976        -------
 977        `aiobungie.iterators.Iterator[aiobungie.crates.SearchableEntity]`
 978            An iterator over the found results matching the provided name.
 979        """
 980        resp = await self.rest.search_entities(name, entity_type, page=page)
 981
 982        return self.factory.deserialize_inventory_results(resp)
 983
 984    # Fireteams
 985
 986    async def fetch_fireteams(
 987        self,
 988        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
 989        *,
 990        platform: typedefs.IntAnd[
 991            fireteams.FireteamPlatform
 992        ] = fireteams.FireteamPlatform.ANY,
 993        language: typing.Union[
 994            fireteams.FireteamLanguage, str
 995        ] = fireteams.FireteamLanguage.ALL,
 996        date_range: int = 0,
 997        page: int = 0,
 998        slots_filter: int = 0,
 999    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1000        """Fetch public Bungie fireteams with open slots.
1001
1002        Parameters
1003        ----------
1004        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1005            The fireteam activity type.
1006
1007        Other Parameters
1008        ----------------
1009        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1010            If this is provided. Then the results will be filtered with the given platform.
1011            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1012        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1013            A locale language to filter the used language in that fireteam.
1014            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1015        date_range : `int`
1016            An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`.
1017        page : `int`
1018            The page number. By default its `0` which returns all available activities.
1019        slots_filter : `int`
1020            Filter the returned fireteams based on available slots. Default is `0`
1021
1022        Returns
1023        -------
1024        `typing.Optional[collections.Sequence[fireteams.Fireteam]]`
1025            A sequence of `aiobungie.crates.Fireteam` or `None`.
1026        """
1027
1028        resp = await self.rest.fetch_fireteams(
1029            activity_type,
1030            platform=platform,
1031            language=language,
1032            date_range=date_range,
1033            page=page,
1034            slots_filter=slots_filter,
1035        )
1036
1037        return self.factory.deserialize_fireteams(resp)
1038
1039    async def fetch_avaliable_clan_fireteams(
1040        self,
1041        access_token: str,
1042        group_id: int,
1043        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1044        *,
1045        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1046        language: typing.Union[fireteams.FireteamLanguage, str],
1047        date_range: int = 0,
1048        page: int = 0,
1049        public_only: bool = False,
1050        slots_filter: int = 0,
1051    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1052        """Fetch a clan's fireteams with open slots.
1053
1054        .. note::
1055            This method requires OAuth2: ReadGroups scope.
1056
1057        Parameters
1058        ----------
1059        access_token : `str`
1060            The bearer access token associated with the bungie account.
1061        group_id : `int`
1062            The group/clan id of the fireteam.
1063        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1064            The fireteam activity type.
1065
1066        Other Parameters
1067        ----------------
1068        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1069            If this is provided. Then the results will be filtered with the given platform.
1070            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1071        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1072            A locale language to filter the used language in that fireteam.
1073            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1074        date_range : `int`
1075            An integer to filter the date range of the returned fireteams. Defaults to `0`.
1076        page : `int`
1077            The page number. By default its `0` which returns all available activities.
1078        public_only: `bool`
1079            If set to True, Then only public fireteams will be returned.
1080        slots_filter : `int`
1081            Filter the returned fireteams based on available slots. Default is `0`
1082
1083        Returns
1084        -------
1085        `typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]`
1086            A sequence of  fireteams found in the clan.
1087            `None` will be returned if nothing was found.
1088        """
1089        resp = await self.rest.fetch_avaliable_clan_fireteams(
1090            access_token,
1091            group_id,
1092            activity_type,
1093            platform=platform,
1094            language=language,
1095            date_range=date_range,
1096            page=page,
1097            public_only=public_only,
1098            slots_filter=slots_filter,
1099        )
1100
1101        return self.factory.deserialize_fireteams(resp)
1102
1103    async def fetch_clan_fireteam(
1104        self, access_token: str, fireteam_id: int, group_id: int
1105    ) -> fireteams.AvailableFireteam:
1106        """Fetch a specific clan fireteam.
1107
1108        .. note::
1109            This method requires OAuth2: ReadGroups scope.
1110
1111        Parameters
1112        ----------
1113        access_token : `str`
1114            The bearer access token associated with the bungie account.
1115        group_id : `int`
1116            The group/clan id to fetch the fireteam from.
1117        fireteam_id : `int`
1118            The fireteam id to fetch.
1119
1120        Returns
1121        -------
1122        `typing.Optional[aiobungie.crates.AvailableFireteam]`
1123            A sequence of available fireteams objects if exists. else `None` will be returned.
1124        """
1125        resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id)
1126
1127        return self.factory.deserialize_available_fireteams(
1128            resp, no_results=True
1129        )  # type: ignore[return-value]
1130
1131    async def fetch_my_clan_fireteams(
1132        self,
1133        access_token: str,
1134        group_id: int,
1135        *,
1136        include_closed: bool = True,
1137        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1138        language: typing.Union[fireteams.FireteamLanguage, str],
1139        filtered: bool = True,
1140        page: int = 0,
1141    ) -> collections.Sequence[fireteams.AvailableFireteam]:
1142        """A method that's similar to `fetch_fireteams` but requires OAuth2.
1143
1144        .. note::
1145            This method requires OAuth2: ReadGroups scope.
1146
1147        Parameters
1148        ----------
1149        access_token : str
1150            The bearer access token associated with the bungie account.
1151        group_id : int
1152            The group/clan id to fetch.
1153
1154        Other Parameters
1155        ----------------
1156        include_closed : bool
1157            If provided and set to True, It will also return closed fireteams.
1158            If provided and set to False, It will only return public fireteams. Default is True.
1159        platform : aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]
1160            If this is provided. Then the results will be filtered with the given platform.
1161            Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
1162        language : typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]
1163            A locale language to filter the used language in that fireteam.
1164            Defaults to aiobungie.crates.FireteamLanguage.ALL
1165        filtered : bool
1166            If set to True, it will filter by clan. Otherwise not. Default is True.
1167        page : int
1168            The page number. By default its 0 which returns all available activities.
1169
1170        Returns
1171        -------
1172        `collections.Sequence[aiobungie.crates.AvailableFireteam]`
1173            A sequence of available fireteams objects if exists. else `None` will be returned.
1174        """
1175        resp = await self.rest.fetch_my_clan_fireteams(
1176            access_token,
1177            group_id,
1178            include_closed=include_closed,
1179            platform=platform,
1180            language=language,
1181            filtered=filtered,
1182            page=page,
1183        )
1184
1185        return self.factory.deserialize_available_fireteams(resp)  # type: ignore[return-value]
1186
1187    # Friends and social.
1188
1189    async def fetch_friends(
1190        self, access_token: str, /
1191    ) -> collections.Sequence[friends.Friend]:
1192        """Fetch bungie friend list.
1193
1194        .. note::
1195            This requests OAuth2: ReadUserData scope.
1196
1197        Parameters
1198        -----------
1199        access_token : `str`
1200            The bearer access token associated with the bungie account.
1201
1202        Returns
1203        -------
1204        `collections.Sequence[aiobungie.crates.Friend]`
1205            A sequence of the friends associated with that access token.
1206        """
1207
1208        resp = await self.rest.fetch_friends(access_token)
1209
1210        return self.factory.deserialize_friends(resp)
1211
1212    async def fetch_friend_requests(
1213        self, access_token: str, /
1214    ) -> friends.FriendRequestView:
1215        """Fetch pending bungie friend requests queue.
1216
1217        .. note::
1218            This requests OAuth2: ReadUserData scope.
1219
1220        Parameters
1221        -----------
1222        access_token : `str`
1223            The bearer access token associated with the bungie account.
1224
1225        Returns
1226        -------
1227        `aiobungie.crates.FriendRequestView`
1228            A friend requests view of that associated access token.
1229        """
1230
1231        resp = await self.rest.fetch_friend_requests(access_token)
1232
1233        return self.factory.deserialize_friend_requests(resp)
1234
1235    # Applications and Developer portal.
1236
1237    async def fetch_application(self, appid: int, /) -> application.Application:
1238        """Fetch a Bungie application.
1239
1240        Parameters
1241        -----------
1242        appid: `int`
1243            The application id.
1244
1245        Returns
1246        --------
1247        `aiobungie.crates.Application`
1248            A Bungie application.
1249        """
1250        resp = await self.rest.fetch_application(appid)
1251
1252        return self.factory.deserialize_app(resp)
1253
1254    # Milestones
1255
1256    async def fetch_public_milestone_content(
1257        self, milestone_hash: int, /
1258    ) -> milestones.MilestoneContent:
1259        """Fetch the milestone content given its hash.
1260
1261        Parameters
1262        ----------
1263        milestone_hash : `int`
1264            The milestone hash.
1265
1266        Returns
1267        -------
1268        `aiobungie.crates.milestones.MilestoneContent`
1269            A milestone content object.
1270        """
1271        resp = await self.rest.fetch_public_milestone_content(milestone_hash)
1272
1273        return self.factory.deserialize_public_milestone_content(resp)

Standard Bungie API client application.

This client deserialize the REST JSON responses using aiobungie.Factory and returns aiobungie.crates Python object implementations of the responses.

A aiobungie.RESTClient REST client can also be used alone for low-level concepts.

Example
import aiobungie

client = aiobungie.Client('...')

async def main():
    async with client.rest:
        user = await client.fetch_current_user_memberships('...')
        print(user)
Parameters
  • token (str): Your Bungie's API key or Token from the developer's portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • client_secret (str | None): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (int | None): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
Client( token: str, /, client_secret: Optional[str] = None, client_id: Optional[int] = None, *, max_retries: int = 4)
100    def __init__(
101        self,
102        token: str,
103        /,
104        client_secret: typing.Optional[str] = None,
105        client_id: typing.Optional[int] = None,
106        *,
107        max_retries: int = 4,
108    ) -> None:
109
110        self._rest = rest_.RESTClient(
111            token,
112            client_secret,
113            client_id,
114            max_retries=max_retries,
115        )
116
117        self._factory = factory_.Factory(self)

Returns the marshalling factory for the client.

rest: aiobungie.interfaces.rest.RESTInterface

Returns the REST client for the this client.

request: aiobungie.Client

A readonly ClientApp instance used for external requests.

metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

A mutable mapping storage for the user's needs.

def run( self, future: collections.abc.Coroutine[typing.Any, None, None], debug: bool = False) -> None:
135    def run(
136        self, future: collections.Coroutine[typing.Any, None, None], debug: bool = False
137    ) -> None:
138        loop: typing.Final[asyncio.AbstractEventLoop] = helpers.get_or_make_loop()
139        try:
140            if not loop.is_running():
141                loop.set_debug(debug)
142                loop.run_until_complete(future)
143
144        except Exception as exc:
145            raise RuntimeError(f"Failed to run {future.__qualname__}") from exc
146
147        except KeyboardInterrupt:
148            _LOG.warn("Unexpected Keyboard interrupt. Exiting.")
149            return

Runs a coroutine function until its complete.

This is equivalent to asyncio.get_event_loop().run_until_complete(...)

Parameters
  • future (collections.Coroutine[None, None, None]): A coroutine object.
  • debug (bool): Either to enable asyncio debug or not. Disabled by default.
Example
async def main() -> None:
    await fetch(...)

# Run the coroutine.
client.run(main())
async def fetch_current_user_memberships(self, access_token: str, /) -> aiobungie.crates.user.User:
153    async def fetch_current_user_memberships(self, access_token: str, /) -> user.User:
154        """Fetch and return a user object of the bungie net user associated with account.
155
156        .. warning::
157            This method requires OAuth2 scope and a Bearer access token.
158
159        Parameters
160        ----------
161        access_token : `str`
162            A valid Bearer access token for the authorization.
163
164        Returns
165        -------
166        `aiobungie.crates.user.User`
167            A user object includes the Destiny memberships and Bungie.net user.
168        """
169        resp = await self.rest.fetch_current_user_memberships(access_token)
170
171        return self.factory.deserialize_user(resp)

Fetch and return a user object of the bungie net user associated with account.

This method requires OAuth2 scope and a Bearer access token.

Parameters
  • access_token (str): A valid Bearer access token for the authorization.
Returns
  • aiobungie.crates.user.User: A user object includes the Destiny memberships and Bungie.net user.
async def fetch_bungie_user(self, id: int, /) -> aiobungie.crates.user.BungieUser:
173    async def fetch_bungie_user(self, id: int, /) -> user.BungieUser:
174        """Fetch a Bungie user by their BungieNet id.
175
176        .. note::
177            This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id`
178            for other memberships.
179
180        Parameters
181        ----------
182        id: `int`
183            The user id.
184
185        Returns
186        -------
187        `aiobungie.crates.user.BungieUser`
188            A Bungie user.
189
190        Raises
191        ------
192        `aiobungie.error.NotFound`
193            The user was not found.
194        """
195        payload = await self.rest.fetch_bungie_user(id)
196
197        return self.factory.deserialize_bungie_user(payload)

Fetch a Bungie user by their BungieNet id.

This returns a Bungie user membership only. Take a look at Client.fetch_membership_from_id for other memberships.

Parameters
  • id (int): The user id.
Returns
  • aiobungie.crates.user.BungieUser: A Bungie user.
Raises
async def search_users( self, name: str, /) -> aiobungie.Iterator[aiobungie.crates.user.SearchableDestinyUser]:
199    async def search_users(
200        self, name: str, /
201    ) -> iterators.Iterator[user.SearchableDestinyUser]:
202        """Search for players and return all players that matches the same name.
203
204        Parameters
205        ----------
206        name : `buildins.str`
207            The user name.
208
209        Returns
210        -------
211        `aiobungie.iterators.Iterator[aiobungie.crates.DestinyMembership]`
212            A sequence of destiny memberships.
213        """
214        payload = await self.rest.search_users(name)
215
216        return iterators.Iterator(
217            [
218                self.factory.deserialize_searched_user(user)
219                for user in payload["searchResults"]
220            ]
221        )

Search for players and return all players that matches the same name.

Parameters
  • name (buildins.str): The user name.
Returns
async def fetch_user_themes(self) -> collections.abc.Sequence[aiobungie.crates.user.UserThemes]:
223    async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]:
224        """Fetch all available user themes.
225
226        Returns
227        -------
228        `collections.Sequence[aiobungie.crates.user.UserThemes]`
229            A sequence of user themes.
230        """
231        data = await self.rest.fetch_user_themes()
232
233        return self.factory.deserialize_user_themes(data)

Fetch all available user themes.

Returns
  • collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of user themes.
async def fetch_hard_types( self, credential: int, type: Union[int, aiobungie.CredentialType] = <CredentialType.STEAMID: 12>, /) -> aiobungie.crates.user.HardLinkedMembership:
235    async def fetch_hard_types(
236        self,
237        credential: int,
238        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
239        /,
240    ) -> user.HardLinkedMembership:
241        """Gets any hard linked membership given a credential.
242        Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now.
243        Cross Save aware.
244
245        Parameters
246        ----------
247        credential: `int`
248            A valid SteamID64
249        type: `aiobungie.CredentialType`
250            The credential type. This must not be changed
251            Since its only credential that works "currently"
252
253        Returns
254        -------
255        `aiobungie.crates.user.HardLinkedMembership`
256            Information about the hard linked data.
257        """
258
259        payload = await self.rest.fetch_hardlinked_credentials(credential, type)
260
261        return user.HardLinkedMembership(
262            id=int(payload["membershipId"]),
263            type=enums.MembershipType(payload["membershipType"]),
264            cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]),
265        )

Gets any hard linked membership given a credential. Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now. Cross Save aware.

Parameters
  • credential (int): A valid SteamID64
  • type (aiobungie.CredentialType): The credential type. This must not be changed Since its only credential that works "currently"
Returns
  • aiobungie.crates.user.HardLinkedMembership: Information about the hard linked data.
async def fetch_membership_from_id( self, id: int, /, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>) -> aiobungie.crates.user.User:
267    async def fetch_membership_from_id(
268        self,
269        id: int,
270        /,
271        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
272    ) -> user.User:
273        """Fetch Bungie user's memberships from their id.
274
275        Notes
276        -----
277        * This returns both BungieNet membership and a sequence of the player's DestinyMemberships
278        Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
279        see `aiobungie.crates.user.DestinyMembership` for more details.
280        * If you only want the bungie user. Consider using `Client.fetch_user` method.
281
282        Parameters
283        ----------
284        id : `int`
285            The user's id.
286        type : `aiobungie.MembershipType`
287            The user's membership type.
288
289        Returns
290        -------
291        `aiobungie.crates.User`
292            A Bungie user with their membership types.
293
294        Raises
295        ------
296        aiobungie.NotFound
297            The requested user was not found.
298        """
299        payload = await self.rest.fetch_membership_from_id(id, type)
300
301        return self.factory.deserialize_user(payload)

Fetch Bungie user's memberships from their id.

Notes
  • This returns both BungieNet membership and a sequence of the player's DestinyMemberships Which includes Stadia, Xbox, Steam and PSN memberships if the player has them, see aiobungie.crates.user.DestinyMembership for more details.
  • If you only want the bungie user. Consider using Client.fetch_user method.
Parameters
Returns
Raises
async def fetch_user_credentials( self, access_token: str, membership_id: int, /) -> collections.abc.Sequence[aiobungie.crates.user.UserCredentials]:
303    async def fetch_user_credentials(
304        self, access_token: str, membership_id: int, /
305    ) -> collections.Sequence[user.UserCredentials]:
306        """Fetch an array of credential types attached to the requested account.
307
308        .. note::
309            This method require OAuth2 Bearer access token.
310
311        Parameters
312        ----------
313        access_token : `str`
314            The bearer access token associated with the bungie account.
315        membership_id : `int`
316            The id of the membership to return.
317
318        Returns
319        -------
320        `collections.Sequence[aiobungie.crates.UserCredentials]`
321            A sequence of the attached user credentials.
322
323        Raises
324        ------
325        `aiobungie.Unauthorized`
326            The access token was wrong or no access token passed.
327        """
328        resp = await self.rest.fetch_user_credentials(access_token, membership_id)
329
330        return self.factory.deserialize_user_credentials(resp)

Fetch an array of credential types attached to the requested account.

This method require OAuth2 Bearer access token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • membership_id (int): The id of the membership to return.
Returns
Raises
async def fetch_profile( self, member_id: int, type: Union[int, aiobungie.MembershipType], components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> aiobungie.crates.components.Component:
334    async def fetch_profile(
335        self,
336        member_id: int,
337        type: typedefs.IntAnd[enums.MembershipType],
338        components: list[enums.ComponentType],
339        auth: typing.Optional[str] = None,
340    ) -> components.Component:
341        """
342        Fetch a bungie profile passing components to the request.
343
344        Parameters
345        ----------
346        member_id: `int`
347            The member's id.
348        type: `aiobungie.MembershipType`
349            A valid membership type.
350        components : `list[aiobungie.ComponentType]`
351            List of profile components to collect and return.
352
353        Other Parameters
354        ----------------
355        auth : `typing.Optional[str]`
356            A Bearer access_token to make the request with.
357            This is optional and limited to components that only requires an Authorization token.
358
359        Returns
360        --------
361        `aiobungie.crates.Component`
362            A Destiny 2 player profile with its components.
363            Only passed components will be available if they exists. Otherwise they will be `None`
364
365        Raises
366        ------
367        `aiobungie.MembershipTypeError`
368            The provided membership type was invalid.
369        """
370        data = await self.rest.fetch_profile(member_id, type, components, auth)
371        return self.factory.deserialize_components(data)

Fetch a bungie profile passing components to the request.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
  • aiobungie.crates.Component: A Destiny 2 player profile with its components. Only passed components will be available if they exists. Otherwise they will be None
Raises
async def fetch_linked_profiles( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, all: bool = False) -> aiobungie.crates.profile.LinkedProfile:
373    async def fetch_linked_profiles(
374        self,
375        member_id: int,
376        member_type: typedefs.IntAnd[enums.MembershipType],
377        /,
378        *,
379        all: bool = False,
380    ) -> profile.LinkedProfile:
381        """Returns a summary information about all profiles linked to the requested member.
382
383        The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
384
385        .. note::
386            It will only return linked accounts whose linkages you are allowed to view.
387
388        Parameters
389        ----------
390        member_id : `int`
391            The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
392        member_type : `aiobungie.MembershipType`
393            The type for the membership whose linked Destiny account you want to return.
394
395        Other Parameters
396        ----------------
397        all : `bool`
398            If provided and set to `True`, All memberships regardless
399            of whether they're obscured by overrides will be returned,
400
401            If provided and set to `False`, Only available memberships will be returned.
402            The default for this is `False`.
403
404        Returns
405        -------
406        `aiobungie.crates.profile.LinkedProfile`
407            A linked profile object.
408        """
409        resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all)
410
411        return self.factory.deserialize_linked_profiles(resp)

Returns a summary information about all profiles linked to the requested member.

The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.

It will only return linked accounts whose linkages you are allowed to view.

Parameters
  • member_id (int): The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID.
  • member_type (aiobungie.MembershipType): The type for the membership whose linked Destiny account you want to return.
Other Parameters
  • all (bool): If provided and set to True, All memberships regardless of whether they're obscured by overrides will be returned,

    If provided and set to False, Only available memberships will be returned. The default for this is False.

Returns
  • aiobungie.crates.profile.LinkedProfile: A linked profile object.
async def fetch_player( self, name: str, code: int, /, type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>) -> collections.abc.Sequence[aiobungie.crates.user.DestinyMembership]:
413    async def fetch_player(
414        self,
415        name: str,
416        code: int,
417        /,
418        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
419    ) -> collections.Sequence[user.DestinyMembership]:
420        """Fetch a Destiny 2 player's memberships.
421
422        Parameters
423        -----------
424        name: `str`
425            The unique Bungie player name.
426        code : `int`
427            The unique Bungie display name code.
428        type: `aiobungie.internal.enums.MembershipType`
429            The player's membership type, e,g. XBOX, STEAM, PSN
430
431        Returns
432        --------
433        `collections.Sequence[aiobungie.crates.DestinyMembership]`
434            A sequence of the found Destiny 2 player memberships.
435            An empty sequence will be returned if no one found.
436
437        Raises
438        ------
439        `aiobungie.MembershipTypeError`
440            The provided membership type was invalid.
441        """
442        resp = await self.rest.fetch_player(name, code, type)
443
444        return self.factory.deserialize_destiny_memberships(resp)

Fetch a Destiny 2 player's memberships.

Parameters
  • name (str): The unique Bungie player name.
  • code (int): The unique Bungie display name code.
  • type (aiobungie.MembershipType): The player's membership type, e,g. XBOX, STEAM, PSN
Returns
Raises
async def fetch_character( self, member_id: int, membership_type: Union[int, aiobungie.MembershipType], character_id: int, components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> aiobungie.crates.components.CharacterComponent:
446    async def fetch_character(
447        self,
448        member_id: int,
449        membership_type: typedefs.IntAnd[enums.MembershipType],
450        character_id: int,
451        components: list[enums.ComponentType],
452        auth: typing.Optional[str] = None,
453    ) -> components.CharacterComponent:
454        """Fetch a Destiny 2 character.
455
456        Parameters
457        ----------
458        member_id: `int`
459            A valid bungie member id.
460        character_id: `int`
461            The Destiny character id to retrieve.
462        membership_type: `aiobungie.internal.enums.MembershipType`
463            The member's membership type.
464        components: `list[aiobungie.ComponentType]`
465            Multiple arguments of character components to collect and return.
466
467        Other Parameters
468        ----------------
469        auth : `typing.Optional[str]`
470            A Bearer access_token to make the request with.
471            This is optional and limited to components that only requires an Authorization token.
472
473        Returns
474        -------
475        `aiobungie.crates.CharacterComponent`
476            A Bungie character component.
477
478        `aiobungie.MembershipTypeError`
479            The provided membership type was invalid.
480        """
481        resp = await self.rest.fetch_character(
482            member_id, membership_type, character_id, components, auth
483        )
484
485        return self.factory.deserialize_character_component(resp)

Fetch a Destiny 2 character.

Parameters
  • member_id (int): A valid bungie member id.
  • character_id (int): The Destiny character id to retrieve.
  • membership_type (aiobungie.MembershipType): The member's membership type.
  • components (list[aiobungie.ComponentType]): Multiple arguments of character components to collect and return.
Other Parameters
  • auth (typing.Optional[str]): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
async def fetch_unique_weapon_history( self, membership_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> collections.abc.Sequence[aiobungie.crates.activity.ExtendedWeaponValues]:
487    async def fetch_unique_weapon_history(
488        self,
489        membership_id: int,
490        character_id: int,
491        membership_type: typedefs.IntAnd[enums.MembershipType],
492    ) -> collections.Sequence[activity.ExtendedWeaponValues]:
493        """Fetch details about unique weapon usage for a character. Includes all exotics.
494
495        Parameters
496        ----------
497        membership_id : `int`
498            The Destiny user membership id.
499        character_id : `int`
500            The character id to retrieve.
501        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
502            The Destiny user's membership type.
503
504        Returns
505        -------
506        `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]`
507            A sequence of the weapon's extended values.
508        """
509        resp = await self._rest.fetch_unique_weapon_history(
510            membership_id, character_id, membership_type
511        )
512
513        return [
514            self._factory.deserialize_extended_weapon_values(weapon)
515            for weapon in resp["weapons"]
516        ]

Fetch details about unique weapon usage for a character. Includes all exotics.

Parameters
Returns
async def fetch_activities( self, member_id: int, character_id: int, mode: Union[int, aiobungie.GameMode], *, membership_type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>, page: int = 0, limit: int = 250) -> aiobungie.Iterator[aiobungie.crates.activity.Activity]:
520    async def fetch_activities(
521        self,
522        member_id: int,
523        character_id: int,
524        mode: typedefs.IntAnd[enums.GameMode],
525        *,
526        membership_type: typedefs.IntAnd[
527            enums.MembershipType
528        ] = enums.MembershipType.ALL,
529        page: int = 0,
530        limit: int = 250,
531    ) -> iterators.Iterator[activity.Activity]:
532        """Fetch a Destiny 2 activity for the specified character id.
533
534        Parameters
535        ----------
536        member_id: `int`
537            The user id that starts with `4611`.
538        character_id: `int`
539            The id of the character to retrieve the activities for.
540        mode: `aiobungie.typedefs.IntAnd[aiobungie.internal.enums.GameMode]`
541            This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
542
543        Other Parameters
544        ----------------
545        membership_type: `aiobungie.internal.enums.MembershipType`
546            The Member ship type, if nothing was passed than it will return all.
547        page: int
548            The page number. Default is `0`
549        limit: int
550            Limit the returned result. Default is `250`.
551
552        Returns
553        -------
554        `aiobungie.iterators.Iterator[aiobungie.crates.Activity]`
555            An iterator of the player's activities.
556
557        Raises
558        ------
559        `aiobungie.MembershipTypeError`
560            The provided membership type was invalid.
561        """
562        resp = await self.rest.fetch_activities(
563            member_id,
564            character_id,
565            mode,
566            membership_type=membership_type,
567            page=page,
568            limit=limit,
569        )
570
571        return self.factory.deserialize_activities(resp)

Fetch a Destiny 2 activity for the specified character id.

Parameters
  • member_id (int): The user id that starts with 4611.
  • character_id (int): The id of the character to retrieve the activities for.
  • mode (aiobungie.typedefs.IntAnd[aiobungie.GameMode]): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
  • membership_type (aiobungie.MembershipType): The Member ship type, if nothing was passed than it will return all.
  • page (int): The page number. Default is 0
  • limit (int): Limit the returned result. Default is 250.
Returns
Raises
async def fetch_post_activity(self, instance_id: int, /) -> aiobungie.crates.activity.PostActivity:
573    async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity:
574        """Fetch a post activity details.
575
576        Parameters
577        ----------
578        instance_id: `int`
579            The activity instance id.
580
581        Returns
582        -------
583        `aiobungie.crates.PostActivity`
584           A post activity object.
585        """
586        resp = await self.rest.fetch_post_activity(instance_id)
587
588        return self.factory.deserialize_post_activity(resp)

Fetch a post activity details.

Parameters
  • instance_id (int): The activity instance id.
Returns
async def fetch_aggregated_activity_stats( self, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> aiobungie.Iterator[aiobungie.crates.activity.AggregatedActivity]:
590    async def fetch_aggregated_activity_stats(
591        self,
592        character_id: int,
593        membership_id: int,
594        membership_type: typedefs.IntAnd[enums.MembershipType],
595    ) -> iterators.Iterator[activity.AggregatedActivity]:
596        """Fetch aggregated activity stats for a character.
597
598        Parameters
599        ----------
600        character_id: `int`
601            The id of the character to retrieve the activities for.
602        membership_id: `int`
603            The id of the user that started with `4611`.
604        membership_type: `aiobungie.internal.enums.MembershipType`
605            The Member ship type.
606
607        Returns
608        -------
609        `aiobungie.iterators.Iterator[aiobungie.crates.AggregatedActivity]`
610            An iterator of the player's activities.
611
612        Raises
613        ------
614        `aiobungie.MembershipTypeError`
615            The provided membership type was invalid.
616        """
617        resp = await self.rest.fetch_aggregated_activity_stats(
618            character_id, membership_id, membership_type
619        )
620
621        return self.factory.deserialize_aggregated_activities(resp)

Fetch aggregated activity stats for a character.

Parameters
  • character_id (int): The id of the character to retrieve the activities for.
  • membership_id (int): The id of the user that started with 4611.
  • membership_type (aiobungie.MembershipType): The Member ship type.
Returns
Raises
async def fetch_clan_from_id( self, id: int, /, access_token: Optional[str] = None) -> aiobungie.crates.clans.Clan:
625    async def fetch_clan_from_id(
626        self,
627        id: int,
628        /,
629        access_token: typing.Optional[str] = None,
630    ) -> clans.Clan:
631        """Fetch a Bungie Clan by its id.
632
633        Parameters
634        -----------
635        id: `int`
636            The clan id.
637
638        Returns
639        --------
640        `aiobungie.crates.Clan`
641            An Bungie clan.
642
643        Raises
644        ------
645        `aiobungie.NotFound`
646            The clan was not found.
647        """
648        resp = await self.rest.fetch_clan_from_id(id, access_token)
649
650        return self.factory.deserialize_clan(resp)

Fetch a Bungie Clan by its id.

Parameters
  • id (int): The clan id.
Returns
Raises
async def fetch_clan( self, name: str, /, access_token: Optional[str] = None, *, type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> aiobungie.crates.clans.Clan:
652    async def fetch_clan(
653        self,
654        name: str,
655        /,
656        access_token: typing.Optional[str] = None,
657        *,
658        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
659    ) -> clans.Clan:
660        """Fetch a Clan by its name.
661        This method will return the first clan found with given name.
662
663        Parameters
664        ----------
665        name: `str`
666            The clan name
667
668        Other Parameters
669        ----------------
670        access_token : `typing.Optional[str]`
671            An optional access token to make the request with.
672
673            If the token was bound to a member of the clan,
674            This field `aiobungie.crates.Clan.current_user_membership` will be available
675            and will return the membership of the user who made this request.
676        type : `aiobungie.GroupType`
677            The group type, Default is aiobungie.GroupType.CLAN.
678
679        Returns
680        -------
681        `aiobungie.crates.Clan`
682            A Bungie clan.
683
684        Raises
685        ------
686        `aiobungie.NotFound`
687            The clan was not found.
688        """
689        resp = await self.rest.fetch_clan(name, access_token, type=type)
690
691        return self.factory.deserialize_clan(resp)

Fetch a Clan by its name. This method will return the first clan found with given name.

Parameters
  • name (str): The clan name
Other Parameters
Returns
Raises
async def fetch_clan_conversations( self, clan_id: int, /) -> collections.abc.Sequence[aiobungie.crates.clans.ClanConversation]:
693    async def fetch_clan_conversations(
694        self, clan_id: int, /
695    ) -> collections.Sequence[clans.ClanConversation]:
696        """Fetch the conversations/chat channels of the given clan id.
697
698        Parameters
699        ----------
700        clan_id : `int`
701            The clan id.
702
703        Returns
704        `collections.Sequence[aiobungie.crates.ClanConversation]`
705            A sequence of the clan chat channels.
706        """
707        resp = await self.rest.fetch_clan_conversations(clan_id)
708
709        return self.factory.deserialize_clan_conversations(resp)

Fetch the conversations/chat channels of the given clan id.

Parameters
async def fetch_clan_admins( self, clan_id: int, /) -> aiobungie.Iterator[aiobungie.crates.clans.ClanMember]:
711    async def fetch_clan_admins(
712        self, clan_id: int, /
713    ) -> iterators.Iterator[clans.ClanMember]:
714        """Fetch the clan founder and admins.
715
716        Parameters
717        ----------
718        clan_id : `int`
719            The clan id.
720
721        Returns
722        -------
723        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
724            An iterator over the found clan admins and founder.
725
726        Raises
727        ------
728        `aiobungie.NotFound`
729            The requested clan was not found.
730        """
731        resp = await self.rest.fetch_clan_admins(clan_id)
732
733        return self.factory.deserialize_clan_members(resp)

Fetch the clan founder and admins.

Parameters
  • clan_id (int): The clan id.
Returns
Raises
async def fetch_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: aiobungie.GroupType = <GroupType.CLAN: 1>) -> collections.abc.Sequence[aiobungie.crates.clans.GroupMember]:
735    async def fetch_groups_for_member(
736        self,
737        member_id: int,
738        member_type: typedefs.IntAnd[enums.MembershipType],
739        /,
740        *,
741        filter: int = 0,
742        group_type: enums.GroupType = enums.GroupType.CLAN,
743    ) -> collections.Sequence[clans.GroupMember]:
744        """Fetch information about the groups that a given member has joined.
745
746        Parameters
747        ----------
748        member_id : `int`
749            The member's id
750        member_type : `aiobungie.MembershipType`
751            The member's membership type.
752
753        Other Parameters
754        ----------------
755        filter : `int`
756            Filter apply to list of joined groups. This Default to `0`
757        group_type : `aiobungie.GroupType`
758            The group's type.
759            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
760
761        Returns
762        -------
763        `collections.Sequence[aiobungie.crates.GroupMember]`
764            A sequence of joined groups for the fetched member.
765        """
766        resp = await self.rest.fetch_groups_for_member(
767            member_id, member_type, filter=filter, group_type=group_type
768        )
769
770        return [
771            self.factory.deserialize_group_member(group) for group in resp["results"]
772        ]

Fetch information about the groups that a given member has joined.

Parameters
Other Parameters
Returns
async def fetch_potential_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> collections.abc.Sequence[aiobungie.crates.clans.GroupMember]:
774    async def fetch_potential_groups_for_member(
775        self,
776        member_id: int,
777        member_type: typedefs.IntAnd[enums.MembershipType],
778        /,
779        *,
780        filter: int = 0,
781        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
782    ) -> collections.Sequence[clans.GroupMember]:
783        """Fetch the potential groups for a clan member.
784
785        Parameters
786        ----------
787        member_id : `int`
788            The member's id
789        member_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
790            The member's membership type.
791
792        Other Parameters
793        ----------------
794        filter : `int`
795            Filter apply to list of joined groups. This Default to `0`
796        group_type : `aiobungie.typedefs.IntAnd[aiobungie.GroupType]`
797            The group's type.
798            This is always set to `aiobungie.GroupType.CLAN` and should not be changed.
799
800        Returns
801        -------
802        `collections.Sequence[aiobungie.crates.GroupMember]`
803            A sequence of joined potential groups for the fetched member.
804        """
805        resp = await self.rest.fetch_potential_groups_for_member(
806            member_id, member_type, filter=filter, group_type=group_type
807        )
808
809        return [
810            self.factory.deserialize_group_member(group) for group in resp["results"]
811        ]

Fetch the potential groups for a clan member.

Parameters
Other Parameters
Returns
async def fetch_clan_members( self, clan_id: int, /, *, name: Optional[str] = None, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>) -> aiobungie.Iterator[aiobungie.crates.clans.ClanMember]:
813    async def fetch_clan_members(
814        self,
815        clan_id: int,
816        /,
817        *,
818        name: typing.Optional[str] = None,
819        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
820    ) -> iterators.Iterator[clans.ClanMember]:
821        """Fetch Bungie clan members.
822
823        Parameters
824        ----------
825        clan_id : `int`
826            The clans id
827
828        Other Parameters
829        ----------------
830        name : `typing.Optional[str]`
831            If provided, Only players matching this name will be returned.
832        type : `aiobungie.MembershipType`
833            An optional clan member's membership type.
834            This parameter is used to filter the returned results
835            by the provided membership, For an example XBox memberships only,
836            Otherwise will return all memberships.
837
838        Returns
839        -------
840        `aiobungie.iterators.Iterator[aiobungie.crates.ClanMember]`
841            An iterator over the bungie clan members.
842
843        Raises
844        ------
845        `aiobungie.NotFound`
846            The clan was not found.
847        """
848        resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name)
849
850        return self.factory.deserialize_clan_members(resp)

Fetch Bungie clan members.

Parameters
  • clan_id (int): The clans id
Other Parameters
  • name (typing.Optional[str]): If provided, Only players matching this name will be returned.
  • type (aiobungie.MembershipType): An optional clan member's membership type. This parameter is used to filter the returned results by the provided membership, For an example XBox memberships only, Otherwise will return all memberships.
Returns
Raises
async def fetch_clan_banners(self) -> collections.abc.Sequence[aiobungie.crates.clans.ClanBanner]:
852    async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]:
853        """Fetch the clan banners.
854
855        Returns
856        -------
857        `collections.Sequence[aiobungie.crates.ClanBanner]`
858            A sequence of the clan banners.
859        """
860        resp = await self.rest.fetch_clan_banners()
861
862        return self.factory.deserialize_clan_banners(resp)

Fetch the clan banners.

Returns
async def kick_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> aiobungie.crates.clans.Clan:
865    async def kick_clan_member(
866        self,
867        access_token: str,
868        /,
869        group_id: int,
870        membership_id: int,
871        membership_type: typedefs.IntAnd[enums.MembershipType],
872    ) -> clans.Clan:
873        """Kick a member from the clan.
874
875        .. note::
876            This request requires OAuth2: oauth2: `AdminGroups` scope.
877
878        Parameters
879        ----------
880        access_token : `str`
881            The bearer access token associated with the bungie account.
882        group_id: `int`
883            The group id.
884        membership_id : `int`
885            The member id to kick.
886        membership_type : `aiobungie.typedefs.IntAnd[aiobungie.MembershipType]`
887            The member's membership type.
888
889        Returns
890        -------
891        `aiobungie.crates.clan.Clan`
892            The clan that the member was kicked from.
893        """
894        resp = await self.rest.kick_clan_member(
895            access_token,
896            group_id=group_id,
897            membership_id=membership_id,
898            membership_type=membership_type,
899        )
900
901        return self.factory.deserialize_clan(resp)

Kick a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to kick.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Returns
  • aiobungie.crates.clan.Clan: The clan that the member was kicked from.
async def fetch_clan_weekly_rewards(self, clan_id: int) -> aiobungie.crates.milestones.Milestone:
903    async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone:
904        """Fetch a Bungie clan's weekly reward state.
905
906        Parameters
907        ----------
908        clan_id : `int`
909            The clan's id.
910
911        Returns
912        -------
913        `aiobungie.crates.Milestone`
914            A runtime status of the clan's milestone data.
915        """
916
917        resp = await self.rest.fetch_clan_weekly_rewards(clan_id)
918
919        return self.factory.deserialize_milestone(resp)

Fetch a Bungie clan's weekly reward state.

Parameters
  • clan_id (int): The clan's id.
Returns
async def fetch_inventory_item(self, hash: int, /) -> aiobungie.crates.entity.InventoryEntity:
923    async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity:
924        """Fetch a static inventory item entity given a its hash.
925
926        Parameters
927        ----------
928        hash: `int`
929            Inventory item's hash.
930
931        Returns
932        -------
933        `aiobungie.crates.InventoryEntity`
934            A bungie inventory item.
935        """
936        resp = await self.rest.fetch_inventory_item(hash)
937
938        return self.factory.deserialize_inventory_entity(resp)

Fetch a static inventory item entity given a its hash.

Parameters
  • hash (int): Inventory item's hash.
Returns
async def fetch_objective_entity(self, hash: int, /) -> aiobungie.crates.entity.ObjectiveEntity:
940    async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity:
941        """Fetch a Destiny objective entity given a its hash.
942
943        Parameters
944        ----------
945        hash: `int`
946            objective's hash.
947
948        Returns
949        -------
950        `aiobungie.crates.ObjectiveEntity`
951            An objective entity item.
952        """
953        resp = await self.rest.fetch_objective_entity(hash)
954
955        return self.factory.deserialize_objective_entity(resp)

Fetch a Destiny objective entity given a its hash.

Parameters
  • hash (int): objective's hash.
Returns
async def search_entities( self, name: str, entity_type: str, *, page: int = 0) -> aiobungie.Iterator[aiobungie.crates.entity.SearchableEntity]:
957    async def search_entities(
958        self, name: str, entity_type: str, *, page: int = 0
959    ) -> iterators.Iterator[entity.SearchableEntity]:
960        """Search for Destiny2 entities given a name and its type.
961
962        Parameters
963        ----------
964        name : `str`
965            The name of the entity, i.e., Thunderlord, One thousand voices.
966        entity_type : `str`
967            The type of the entity, AKA Definition,
968            For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items.
969
970        Other Parameters
971        ----------------
972        page : `int`
973            An optional page to return. Default to 0.
974
975        Returns
976        -------
977        `aiobungie.iterators.Iterator[aiobungie.crates.SearchableEntity]`
978            An iterator over the found results matching the provided name.
979        """
980        resp = await self.rest.search_entities(name, entity_type, page=page)
981
982        return self.factory.deserialize_inventory_results(resp)

Search for Destiny2 entities given a name and its type.

Parameters
  • name (str): The name of the entity, i.e., Thunderlord, One thousand voices.
  • entity_type (str): The type of the entity, AKA Definition, For an example DestinyInventoryItemDefinition for emblems, weapons, and other inventory items.
Other Parameters
  • page (int): An optional page to return. Default to 0.
Returns
async def fetch_fireteams( self, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform] = <FireteamPlatform.ANY: 0>, language: Union[aiobungie.FireteamLanguage, str] = <FireteamLanguage.ALL: >, date_range: int = 0, page: int = 0, slots_filter: int = 0) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
 986    async def fetch_fireteams(
 987        self,
 988        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
 989        *,
 990        platform: typedefs.IntAnd[
 991            fireteams.FireteamPlatform
 992        ] = fireteams.FireteamPlatform.ANY,
 993        language: typing.Union[
 994            fireteams.FireteamLanguage, str
 995        ] = fireteams.FireteamLanguage.ALL,
 996        date_range: int = 0,
 997        page: int = 0,
 998        slots_filter: int = 0,
 999    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1000        """Fetch public Bungie fireteams with open slots.
1001
1002        Parameters
1003        ----------
1004        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1005            The fireteam activity type.
1006
1007        Other Parameters
1008        ----------------
1009        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1010            If this is provided. Then the results will be filtered with the given platform.
1011            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1012        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1013            A locale language to filter the used language in that fireteam.
1014            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1015        date_range : `int`
1016            An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`.
1017        page : `int`
1018            The page number. By default its `0` which returns all available activities.
1019        slots_filter : `int`
1020            Filter the returned fireteams based on available slots. Default is `0`
1021
1022        Returns
1023        -------
1024        `typing.Optional[collections.Sequence[fireteams.Fireteam]]`
1025            A sequence of `aiobungie.crates.Fireteam` or `None`.
1026        """
1027
1028        resp = await self.rest.fetch_fireteams(
1029            activity_type,
1030            platform=platform,
1031            language=language,
1032            date_range=date_range,
1033            page=page,
1034            slots_filter=slots_filter,
1035        )
1036
1037        return self.factory.deserialize_fireteams(resp)

Fetch public Bungie fireteams with open slots.

Parameters
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (int): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_avaliable_clan_fireteams( self, access_token: str, group_id: int, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], date_range: int = 0, page: int = 0, public_only: bool = False, slots_filter: int = 0) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
1039    async def fetch_avaliable_clan_fireteams(
1040        self,
1041        access_token: str,
1042        group_id: int,
1043        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1044        *,
1045        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1046        language: typing.Union[fireteams.FireteamLanguage, str],
1047        date_range: int = 0,
1048        page: int = 0,
1049        public_only: bool = False,
1050        slots_filter: int = 0,
1051    ) -> typing.Optional[collections.Sequence[fireteams.Fireteam]]:
1052        """Fetch a clan's fireteams with open slots.
1053
1054        .. note::
1055            This method requires OAuth2: ReadGroups scope.
1056
1057        Parameters
1058        ----------
1059        access_token : `str`
1060            The bearer access token associated with the bungie account.
1061        group_id : `int`
1062            The group/clan id of the fireteam.
1063        activity_type : `aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]`
1064            The fireteam activity type.
1065
1066        Other Parameters
1067        ----------------
1068        platform : `aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]`
1069            If this is provided. Then the results will be filtered with the given platform.
1070            Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms.
1071        language : `typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]`
1072            A locale language to filter the used language in that fireteam.
1073            Defaults to `aiobungie.crates.FireteamLanguage.ALL`
1074        date_range : `int`
1075            An integer to filter the date range of the returned fireteams. Defaults to `0`.
1076        page : `int`
1077            The page number. By default its `0` which returns all available activities.
1078        public_only: `bool`
1079            If set to True, Then only public fireteams will be returned.
1080        slots_filter : `int`
1081            Filter the returned fireteams based on available slots. Default is `0`
1082
1083        Returns
1084        -------
1085        `typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]`
1086            A sequence of  fireteams found in the clan.
1087            `None` will be returned if nothing was found.
1088        """
1089        resp = await self.rest.fetch_avaliable_clan_fireteams(
1090            access_token,
1091            group_id,
1092            activity_type,
1093            platform=platform,
1094            language=language,
1095            date_range=date_range,
1096            page=page,
1097            public_only=public_only,
1098            slots_filter=slots_filter,
1099        )
1100
1101        return self.factory.deserialize_fireteams(resp)

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id of the fireteam.
  • activity_type (aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]): The fireteam activity type.
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (int): An integer to filter the date range of the returned fireteams. Defaults to 0.
  • page (int): The page number. By default its 0 which returns all available activities.
  • public_only (bool): If set to True, Then only public fireteams will be returned.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
  • typing.Optional[collections.Sequence[aiobungie.crates.Fireteam]]: A sequence of fireteams found in the clan. None will be returned if nothing was found.
async def fetch_clan_fireteam( self, access_token: str, fireteam_id: int, group_id: int) -> aiobungie.crates.fireteams.AvailableFireteam:
1103    async def fetch_clan_fireteam(
1104        self, access_token: str, fireteam_id: int, group_id: int
1105    ) -> fireteams.AvailableFireteam:
1106        """Fetch a specific clan fireteam.
1107
1108        .. note::
1109            This method requires OAuth2: ReadGroups scope.
1110
1111        Parameters
1112        ----------
1113        access_token : `str`
1114            The bearer access token associated with the bungie account.
1115        group_id : `int`
1116            The group/clan id to fetch the fireteam from.
1117        fireteam_id : `int`
1118            The fireteam id to fetch.
1119
1120        Returns
1121        -------
1122        `typing.Optional[aiobungie.crates.AvailableFireteam]`
1123            A sequence of available fireteams objects if exists. else `None` will be returned.
1124        """
1125        resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id)
1126
1127        return self.factory.deserialize_available_fireteams(
1128            resp, no_results=True
1129        )  # type: ignore[return-value]

Fetch a specific clan fireteam.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch the fireteam from.
  • fireteam_id (int): The fireteam id to fetch.
Returns
async def fetch_my_clan_fireteams( self, access_token: str, group_id: int, *, include_closed: bool = True, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], filtered: bool = True, page: int = 0) -> collections.abc.Sequence[aiobungie.crates.fireteams.AvailableFireteam]:
1131    async def fetch_my_clan_fireteams(
1132        self,
1133        access_token: str,
1134        group_id: int,
1135        *,
1136        include_closed: bool = True,
1137        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1138        language: typing.Union[fireteams.FireteamLanguage, str],
1139        filtered: bool = True,
1140        page: int = 0,
1141    ) -> collections.Sequence[fireteams.AvailableFireteam]:
1142        """A method that's similar to `fetch_fireteams` but requires OAuth2.
1143
1144        .. note::
1145            This method requires OAuth2: ReadGroups scope.
1146
1147        Parameters
1148        ----------
1149        access_token : str
1150            The bearer access token associated with the bungie account.
1151        group_id : int
1152            The group/clan id to fetch.
1153
1154        Other Parameters
1155        ----------------
1156        include_closed : bool
1157            If provided and set to True, It will also return closed fireteams.
1158            If provided and set to False, It will only return public fireteams. Default is True.
1159        platform : aiobungie.typedefs.IntAnd[aiobungie.crates.fireteams.FireteamPlatform]
1160            If this is provided. Then the results will be filtered with the given platform.
1161            Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
1162        language : typing.Union[aiobungie.crates.fireteams.FireteamLanguage, str]
1163            A locale language to filter the used language in that fireteam.
1164            Defaults to aiobungie.crates.FireteamLanguage.ALL
1165        filtered : bool
1166            If set to True, it will filter by clan. Otherwise not. Default is True.
1167        page : int
1168            The page number. By default its 0 which returns all available activities.
1169
1170        Returns
1171        -------
1172        `collections.Sequence[aiobungie.crates.AvailableFireteam]`
1173            A sequence of available fireteams objects if exists. else `None` will be returned.
1174        """
1175        resp = await self.rest.fetch_my_clan_fireteams(
1176            access_token,
1177            group_id,
1178            include_closed=include_closed,
1179            platform=platform,
1180            language=language,
1181            filtered=filtered,
1182            page=page,
1183        )
1184
1185        return self.factory.deserialize_available_fireteams(resp)  # type: ignore[return-value]

A method that's similar to fetch_fireteams but requires OAuth2.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch.
Other Parameters
  • include_closed (bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True.
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • filtered (bool): If set to True, it will filter by clan. Otherwise not. Default is True.
  • page (int): The page number. By default its 0 which returns all available activities.
Returns
async def fetch_friends( self, access_token: str, /) -> collections.abc.Sequence[aiobungie.crates.friends.Friend]:
1189    async def fetch_friends(
1190        self, access_token: str, /
1191    ) -> collections.Sequence[friends.Friend]:
1192        """Fetch bungie friend list.
1193
1194        .. note::
1195            This requests OAuth2: ReadUserData scope.
1196
1197        Parameters
1198        -----------
1199        access_token : `str`
1200            The bearer access token associated with the bungie account.
1201
1202        Returns
1203        -------
1204        `collections.Sequence[aiobungie.crates.Friend]`
1205            A sequence of the friends associated with that access token.
1206        """
1207
1208        resp = await self.rest.fetch_friends(access_token)
1209
1210        return self.factory.deserialize_friends(resp)

Fetch bungie friend list.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_friend_requests(self, access_token: str, /) -> aiobungie.crates.friends.FriendRequestView:
1212    async def fetch_friend_requests(
1213        self, access_token: str, /
1214    ) -> friends.FriendRequestView:
1215        """Fetch pending bungie friend requests queue.
1216
1217        .. note::
1218            This requests OAuth2: ReadUserData scope.
1219
1220        Parameters
1221        -----------
1222        access_token : `str`
1223            The bearer access token associated with the bungie account.
1224
1225        Returns
1226        -------
1227        `aiobungie.crates.FriendRequestView`
1228            A friend requests view of that associated access token.
1229        """
1230
1231        resp = await self.rest.fetch_friend_requests(access_token)
1232
1233        return self.factory.deserialize_friend_requests(resp)

Fetch pending bungie friend requests queue.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_application(self, appid: int, /) -> aiobungie.crates.application.Application:
1237    async def fetch_application(self, appid: int, /) -> application.Application:
1238        """Fetch a Bungie application.
1239
1240        Parameters
1241        -----------
1242        appid: `int`
1243            The application id.
1244
1245        Returns
1246        --------
1247        `aiobungie.crates.Application`
1248            A Bungie application.
1249        """
1250        resp = await self.rest.fetch_application(appid)
1251
1252        return self.factory.deserialize_app(resp)

Fetch a Bungie application.

Parameters
  • appid (int): The application id.
Returns
async def fetch_public_milestone_content( self, milestone_hash: int, /) -> aiobungie.crates.milestones.MilestoneContent:
1256    async def fetch_public_milestone_content(
1257        self, milestone_hash: int, /
1258    ) -> milestones.MilestoneContent:
1259        """Fetch the milestone content given its hash.
1260
1261        Parameters
1262        ----------
1263        milestone_hash : `int`
1264            The milestone hash.
1265
1266        Returns
1267        -------
1268        `aiobungie.crates.milestones.MilestoneContent`
1269            A milestone content object.
1270        """
1271        resp = await self.rest.fetch_public_milestone_content(milestone_hash)
1272
1273        return self.factory.deserialize_public_milestone_content(resp)

Fetch the milestone content given its hash.

Parameters
  • milestone_hash (int): The milestone hash.
Returns
  • aiobungie.crates.milestones.MilestoneContent: A milestone content object.
@typing.final
class ClosedReasons(aiobungie.Flag):
779@typing.final
780class ClosedReasons(Flag):
781    """A Flags enumeration representing the reasons why a person can't join this user's fireteam."""
782
783    NONE = 0
784    MATCHMAKING = 1 << 0
785    LOADING = 1 << 1
786    SOLO = 1 << 2
787    """The activity is required to be played solo."""
788    INTERNAL_REASONS = 1 << 3
789    """
790    The user can't be joined for one of a variety of internal reasons.
791    Basically, the game can't let you join at this time,
792    but for reasons that aren't under the control of this user
793    """
794    DISALLOWED_BY_GAME_STATE = 1 << 4
795    """The user's current activity/quest/other transitory game state is preventing joining."""
796    OFFLINE = 32768
797    """The user appears offline."""

A Flags enumeration representing the reasons why a person can't join this user's fireteam.

NONE = <ClosedReasons.NONE: 0>
MATCHMAKING = <ClosedReasons.MATCHMAKING: 1>
LOADING = <ClosedReasons.LOADING: 2>
SOLO = <ClosedReasons.SOLO: 4>

The activity is required to be played solo.

INTERNAL_REASONS = <ClosedReasons.INTERNAL_REASONS: 8>

The user can't be joined for one of a variety of internal reasons. Basically, the game can't let you join at this time, but for reasons that aren't under the control of this user

DISALLOWED_BY_GAME_STATE = <ClosedReasons.DISALLOWED_BY_GAME_STATE: 16>

The user's current activity/quest/other transitory game state is preventing joining.

OFFLINE = <ClosedReasons.OFFLINE: 32768>

The user appears offline.

Inherited Members
Flag
name
value
@typing.final
class ComponentFields(aiobungie.Enum):
74@typing.final
75class ComponentFields(enums.Enum):
76    """An enum that provides fields found in a base component response."""
77
78    PRIVACY = ComponentPrivacy
79    DISABLED = False

An enum that provides fields found in a base component response.

PRIVACY = <ComponentFields.PRIVACY: <enum 'ComponentPrivacy'>>
DISABLED = <ComponentFields.DISABLED: False>
Inherited Members
Enum
name
value
@typing.final
class ComponentPrivacy(builtins.int, aiobungie.Enum):
65@typing.final
66class ComponentPrivacy(int, enums.Enum):
67    """An enum the provides privacy settings for profile components."""
68
69    NONE = 0
70    PUBLIC = 1
71    PRIVATE = 2

An enum the provides privacy settings for profile components.

NONE = <ComponentPrivacy.NONE: 0>
PUBLIC = <ComponentPrivacy.PUBLIC: 1>
PRIVATE = <ComponentPrivacy.PRIVATE: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ComponentType(aiobungie.Enum):
358@typing.final
359class ComponentType(Enum):
360    """An Enum for Destiny 2 profile Components."""
361
362    NONE = 0
363
364    PROFILE = 100
365    PROFILE_INVENTORIES = 102
366    PROFILE_CURRENCIES = 103
367    PROFILE_PROGRESSION = 104
368    ALL_PROFILES = (
369        PROFILE,
370        PROFILE_INVENTORIES,
371        PROFILE_CURRENCIES,
372        PROFILE_PROGRESSION,
373    )
374    """All profile components."""
375
376    VENDORS = 400
377    VENDOR_SALES = 402
378    VENDOR_RECEIPTS = 101
379    ALL_VENDORS = (VENDORS, VENDOR_RECEIPTS, VENDOR_SALES)
380    """All vendor components."""
381
382    # Items
383    ITEM_INSTANCES = 300
384    ITEM_OBJECTIVES = 301
385    ITEM_PERKS = 302
386    ITEM_RENDER_DATA = 303
387    ITEM_STATS = 304
388    ITEM_SOCKETS = 305
389    ITEM_TALENT_GRINDS = 306
390    ITEM_PLUG_STATES = 308
391    ITEM_PLUG_OBJECTIVES = 309
392    ITEM_REUSABLE_PLUGS = 310
393
394    ALL_ITEMS = (
395        ITEM_PLUG_OBJECTIVES,
396        ITEM_PLUG_STATES,
397        ITEM_SOCKETS,
398        ITEM_INSTANCES,
399        ITEM_OBJECTIVES,
400        ITEM_PERKS,
401        ITEM_RENDER_DATA,
402        ITEM_STATS,
403        ITEM_TALENT_GRINDS,
404        ITEM_REUSABLE_PLUGS,
405    )
406    """All item components."""
407
408    PLATFORM_SILVER = 105
409    KIOSKS = 500
410    CURRENCY_LOOKUPS = 600
411    PRESENTATION_NODES = 700
412    COLLECTIBLES = 800
413    RECORDS = 900
414    TRANSITORY = 1000
415    METRICS = 1100
416    INVENTORIES = 102
417    STRING_VARIABLES = 1200
418    CRAFTABLES = 1300
419
420    CHARACTERS = 200
421    CHARACTER_INVENTORY = 201
422    CHARECTER_PROGRESSION = 202
423    CHARACTER_RENDER_DATA = 203
424    CHARACTER_ACTIVITIES = 204
425    CHARACTER_EQUIPMENT = 205
426
427    ALL_CHARACTERS = (
428        CHARACTERS,
429        CHARACTER_INVENTORY,
430        CHARECTER_PROGRESSION,
431        CHARACTER_RENDER_DATA,
432        CHARACTER_ACTIVITIES,
433        CHARACTER_EQUIPMENT,
434        RECORDS,
435    )
436    """All character components."""
437
438    ALL = (
439        *ALL_PROFILES,  # type: ignore
440        *ALL_CHARACTERS,  # type: ignore
441        *ALL_VENDORS,  # type: ignore
442        *ALL_ITEMS,  # type: ignore
443        RECORDS,
444        CURRENCY_LOOKUPS,
445        PRESENTATION_NODES,
446        COLLECTIBLES,
447        KIOSKS,
448        METRICS,
449        PLATFORM_SILVER,
450        INVENTORIES,
451        STRING_VARIABLES,
452        TRANSITORY,
453        CRAFTABLES,
454    )
455    """ALl components included."""

An Enum for Destiny 2 profile Components.

NONE = <ComponentType.NONE: 0>
PROFILE = <ComponentType.PROFILE: 100>
PROFILE_INVENTORIES = <ComponentType.PROFILE_INVENTORIES: 102>
PROFILE_CURRENCIES = <ComponentType.PROFILE_CURRENCIES: 103>
PROFILE_PROGRESSION = <ComponentType.PROFILE_PROGRESSION: 104>
ALL_PROFILES = <ComponentType.ALL_PROFILES: (100, 102, 103, 104)>

All profile components.

VENDORS = <ComponentType.VENDORS: 400>
VENDOR_SALES = <ComponentType.VENDOR_SALES: 402>
VENDOR_RECEIPTS = <ComponentType.VENDOR_RECEIPTS: 101>
ALL_VENDORS = <ComponentType.ALL_VENDORS: (400, 101, 402)>

All vendor components.

ITEM_INSTANCES = <ComponentType.ITEM_INSTANCES: 300>
ITEM_OBJECTIVES = <ComponentType.ITEM_OBJECTIVES: 301>
ITEM_PERKS = <ComponentType.ITEM_PERKS: 302>
ITEM_RENDER_DATA = <ComponentType.ITEM_RENDER_DATA: 303>
ITEM_STATS = <ComponentType.ITEM_STATS: 304>
ITEM_SOCKETS = <ComponentType.ITEM_SOCKETS: 305>
ITEM_TALENT_GRINDS = <ComponentType.ITEM_TALENT_GRINDS: 306>
ITEM_PLUG_STATES = <ComponentType.ITEM_PLUG_STATES: 308>
ITEM_PLUG_OBJECTIVES = <ComponentType.ITEM_PLUG_OBJECTIVES: 309>
ITEM_REUSABLE_PLUGS = <ComponentType.ITEM_REUSABLE_PLUGS: 310>
ALL_ITEMS = <ComponentType.ALL_ITEMS: (309, 308, 305, 300, 301, 302, 303, 304, 306, 310)>

All item components.

PLATFORM_SILVER = <ComponentType.PLATFORM_SILVER: 105>
KIOSKS = <ComponentType.KIOSKS: 500>
CURRENCY_LOOKUPS = <ComponentType.CURRENCY_LOOKUPS: 600>
PRESENTATION_NODES = <ComponentType.PRESENTATION_NODES: 700>
COLLECTIBLES = <ComponentType.COLLECTIBLES: 800>
RECORDS = <ComponentType.RECORDS: 900>
TRANSITORY = <ComponentType.TRANSITORY: 1000>
METRICS = <ComponentType.METRICS: 1100>
INVENTORIES = <ComponentType.PROFILE_INVENTORIES: 102>
STRING_VARIABLES = <ComponentType.STRING_VARIABLES: 1200>
CRAFTABLES = <ComponentType.CRAFTABLES: 1300>
CHARACTERS = <ComponentType.CHARACTERS: 200>
CHARACTER_INVENTORY = <ComponentType.CHARACTER_INVENTORY: 201>
CHARECTER_PROGRESSION = <ComponentType.CHARECTER_PROGRESSION: 202>
CHARACTER_RENDER_DATA = <ComponentType.CHARACTER_RENDER_DATA: 203>
CHARACTER_ACTIVITIES = <ComponentType.CHARACTER_ACTIVITIES: 204>
CHARACTER_EQUIPMENT = <ComponentType.CHARACTER_EQUIPMENT: 205>
ALL_CHARACTERS = <ComponentType.ALL_CHARACTERS: (200, 201, 202, 203, 204, 205, 900)>

All character components.

ALL = <ComponentType.ALL: (100, 102, 103, 104, 200, 201, 202, 203, 204, 205, 900, 400, 101, 402, 309, 308, 305, 300, 301, 302, 303, 304, 306, 310, 900, 600, 700, 800, 500, 1100, 105, 102, 1200, 1000, 1300)>

ALl components included.

Inherited Members
Enum
name
value
@typing.final
class CredentialType(builtins.int, aiobungie.Enum):
661@typing.final
662class CredentialType(int, Enum):
663    """The types of the accounts system supports at bungie."""
664
665    NONE = 0
666    XUID = 1
667    PSNID = 2
668    WILD = 3
669    FAKE = 4
670    FACEBOOK = 5
671    GOOGLE = 8
672    WINDOWS = 9
673    DEMONID = 10
674    STEAMID = 12
675    BATTLENETID = 14
676    STADIAID = 16
677    TWITCHID = 18

The types of the accounts system supports at bungie.

NONE = <CredentialType.NONE: 0>
XUID = <CredentialType.XUID: 1>
PSNID = <CredentialType.PSNID: 2>
WILD = <CredentialType.WILD: 3>
FAKE = <CredentialType.FAKE: 4>
FACEBOOK = <CredentialType.FACEBOOK: 5>
GOOGLE = <CredentialType.GOOGLE: 8>
WINDOWS = <CredentialType.WINDOWS: 9>
DEMONID = <CredentialType.DEMONID: 10>
STEAMID = <CredentialType.STEAMID: 12>
BATTLENETID = <CredentialType.BATTLENETID: 14>
STADIAID = <CredentialType.STADIAID: 16>
TWITCHID = <CredentialType.TWITCHID: 18>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class DamageType(builtins.int, aiobungie.Enum):
539@typing.final
540class DamageType(int, Enum):
541    """Enums for Destiny Damage types"""
542
543    NONE = 0
544    KINETIC = 1
545    ARC = 2
546    SOLAR = 3
547    VOID = 4
548    RAID = 5
549    """This is a special damage type reserved for some raid activity encounters."""
550    STASIS = 6

Enums for Destiny Damage types

NONE = <DamageType.NONE: 0>
KINETIC = <DamageType.KINETIC: 1>
ARC = <DamageType.ARC: 2>
SOLAR = <DamageType.SOLAR: 3>
VOID = <DamageType.VOID: 4>
RAID = <DamageType.RAID: 5>

This is a special damage type reserved for some raid activity encounters.

STASIS = <DamageType.STASIS: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Difficulty(builtins.int, aiobungie.Enum):
64@typing.final
65class Difficulty(int, enums.Enum):
66    """An enum for activities difficulties."""
67
68    TRIVIAL = 0
69    EASY = 1
70    NORMAL = 2
71    CHALLENGING = 3
72    HARD = 4
73    BRAVE = 5
74    ALMOST_IMPOSSIBLE = 6
75    IMPOSSIBLE = 7

An enum for activities difficulties.

TRIVIAL = <Difficulty.TRIVIAL: 0>
EASY = <Difficulty.EASY: 1>
NORMAL = <Difficulty.NORMAL: 2>
CHALLENGING = <Difficulty.CHALLENGING: 3>
HARD = <Difficulty.HARD: 4>
BRAVE = <Difficulty.BRAVE: 5>
ALMOST_IMPOSSIBLE = <Difficulty.ALMOST_IMPOSSIBLE: 6>
IMPOSSIBLE = <Difficulty.IMPOSSIBLE: 7>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Dungeon(builtins.int, aiobungie.Enum):
160@typing.final
161class Dungeon(int, Enum):
162    """An Enum for all available Dungeon/Like missions in Destiny 2."""
163
164    NORMAL_PRESAGE = 2124066889
165    """Normal Presage"""
166
167    MASTER_PRESAGE = 4212753278
168    """Master Presage"""
169
170    HARBINGER = 1738383283
171    """Harbinger"""
172
173    PROPHECY = 4148187374
174    """Prophecy"""
175
176    MASTER_POH = 785700673
177    """Master Pit of Heresy?"""
178
179    LEGEND_POH = 785700678
180    """Legend Pit of Heresy?"""
181
182    POH = 1375089621
183    """Normal Pit of Heresy."""
184
185    SHATTERED = 2032534090
186    """Shattered Throne"""
187
188    GOA_LEGEND = 4078656646
189    """Grasp of Avarice legend."""
190
191    GOA_MASTER = 3774021532
192    """Grasp of Avarice master."""

An Enum for all available Dungeon/Like missions in Destiny 2.

NORMAL_PRESAGE = <Dungeon.NORMAL_PRESAGE: 2124066889>

Normal Presage

MASTER_PRESAGE = <Dungeon.MASTER_PRESAGE: 4212753278>

Master Presage

HARBINGER = <Dungeon.HARBINGER: 1738383283>

Harbinger

PROPHECY = <Dungeon.PROPHECY: 4148187374>

Prophecy

MASTER_POH = <Dungeon.MASTER_POH: 785700673>

Master Pit of Heresy?

LEGEND_POH = <Dungeon.LEGEND_POH: 785700678>

Legend Pit of Heresy?

POH = <Dungeon.POH: 1375089621>

Normal Pit of Heresy.

SHATTERED = <Dungeon.SHATTERED: 2032534090>

Shattered Throne

GOA_LEGEND = <Dungeon.GOA_LEGEND: 4078656646>

Grasp of Avarice legend.

GOA_MASTER = <Dungeon.GOA_MASTER: 3774021532>

Grasp of Avarice master.

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Enum(enum.Enum):
72class Enum(__enum.Enum):
73    """Builtin Python enum with extra handlings."""
74
75    @property
76    def name(self) -> str:  # type: ignore[override]
77        return self._name_
78
79    @property
80    def value(self) -> typing.Any:  # type: ignore[override]
81        return self._value_
82
83    def __str__(self) -> str:
84        return self._name_
85
86    def __repr__(self) -> str:
87        return f"<{type(self).__name__}.{self._name_}: {self._value_!s}>"
88
89    def __int__(self) -> int:
90        if isinstance(self.value, _ITERABLE):
91            raise TypeError(
92                f"Can't overload {self.value} in {type(self).__name__}, Please use `.value` attribute.",
93            )
94        return int(self.value)

Builtin Python enum with extra handlings.

name: str

The name of the Enum member.

value: Any

The value of the Enum member.

class Factory(aiobungie.interfaces.factory.FactoryInterface):
  61class Factory(interfaces.FactoryInterface):
  62    """The base deserialization factory class for all aiobungie objects.
  63
  64    Highly inspired hikari entity factory used to deserialize JSON responses from the REST client and turning them
  65    into a `aiobungie.crates` Python classes.
  66    """
  67
  68    __slots__ = ("_net",)
  69
  70    def __init__(self, net: traits.Netrunner) -> None:
  71        self._net = net
  72
  73    def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser:
  74        return user.BungieUser(
  75            id=int(data["membershipId"]),
  76            created_at=time.clean_date(data["firstAccess"]),
  77            name=data.get("cachedBungieGlobalDisplayName", undefined.UNDEFINED),
  78            is_deleted=data["isDeleted"],
  79            about=data["about"],
  80            updated_at=time.clean_date(data["lastUpdate"]),
  81            psn_name=data.get("psnDisplayName", None),
  82            stadia_name=data.get("stadiaDisplayName", None),
  83            steam_name=data.get("steamDisplayName", None),
  84            twitch_name=data.get("twitchDisplayName", None),
  85            blizzard_name=data.get("blizzardDisplayName", None),
  86            status=data["statusText"],
  87            locale=data["locale"],
  88            picture=assets.Image(path=str(data["profilePicturePath"])),
  89            code=data.get("cachedBungieGlobalDisplayNameCode", None),
  90            unique_name=data.get("uniqueName", None),
  91            theme_id=int(data["profileTheme"]),
  92            show_activity=bool(data["showActivity"]),
  93            theme_name=data["profileThemeName"],
  94            display_title=data["userTitleDisplay"],
  95        )
  96
  97    def deserialize_partial_bungie_user(
  98        self, payload: typedefs.JSONObject
  99    ) -> user.PartialBungieUser:
 100        return user.PartialBungieUser(
 101            net=self._net,
 102            types=[
 103                enums.MembershipType(type_)
 104                for type_ in payload.get("applicableMembershipTypes", [])
 105            ],
 106            name=payload.get("displayName", undefined.UNDEFINED),
 107            id=int(payload["membershipId"]),
 108            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
 109            is_public=payload["isPublic"],
 110            icon=assets.Image(payload.get("iconPath", "")),
 111            type=enums.MembershipType(payload["membershipType"]),
 112        )
 113
 114    def deserialize_destiny_membership(
 115        self, payload: typedefs.JSONObject
 116    ) -> user.DestinyMembership:
 117        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
 118        if (
 119            raw_name := payload.get("bungieGlobalDisplayName", "")
 120        ) and not typedefs.is_unknown(raw_name):
 121            name = raw_name
 122
 123        return user.DestinyMembership(
 124            net=self._net,
 125            id=int(payload["membershipId"]),
 126            name=name,
 127            code=payload.get("bungieGlobalDisplayNameCode", None),
 128            last_seen_name=payload.get("LastSeenDisplayName")
 129            or payload.get("displayName")  # noqa: W503
 130            or "",  # noqa: W503
 131            type=enums.MembershipType(payload["membershipType"]),
 132            is_public=payload["isPublic"],
 133            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
 134            icon=assets.Image(payload.get("iconPath", "")),
 135            types=[
 136                enums.MembershipType(type_)
 137                for type_ in payload.get("applicableMembershipTypes", [])
 138            ],
 139        )
 140
 141    def deserialize_destiny_memberships(
 142        self, data: typedefs.JSONArray
 143    ) -> collections.Sequence[user.DestinyMembership]:
 144        return [self.deserialize_destiny_membership(membership) for membership in data]
 145
 146    def deserialize_user(self, data: typedefs.JSONObject) -> user.User:
 147
 148        primary_membership_id: typing.Optional[int] = None
 149        if raw_primary_id := data.get("primaryMembershipId"):
 150            primary_membership_id = int(raw_primary_id)
 151
 152        return user.User(
 153            bungie=self.deserialize_bungie_user(data["bungieNetUser"]),
 154            destiny=self.deserialize_destiny_memberships(data["destinyMemberships"]),
 155            primary_membership_id=primary_membership_id,
 156        )
 157
 158    def deserialize_searched_user(
 159        self, payload: typedefs.JSONObject
 160    ) -> user.SearchableDestinyUser:
 161        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
 162        if (raw_name := payload["bungieGlobalDisplayName"]) and not typedefs.is_unknown(
 163            raw_name
 164        ):
 165            name = raw_name
 166
 167        code: typing.Optional[int] = None
 168        if raw_code := payload.get("bungieGlobalDisplayNameCode"):
 169            code = int(raw_code)
 170
 171        bungie_id: typing.Optional[int] = None
 172        if raw_bungie_id := payload.get("bungieNetMembershipId"):
 173            bungie_id = int(raw_bungie_id)
 174
 175        return user.SearchableDestinyUser(
 176            name=name,
 177            code=code,
 178            bungie_id=bungie_id,
 179            memberships=self.deserialize_destiny_memberships(
 180                payload["destinyMemberships"]
 181            ),
 182        )
 183
 184    def deserialize_user_credentials(
 185        self, payload: typedefs.JSONArray
 186    ) -> collections.Sequence[user.UserCredentials]:
 187        return [
 188            user.UserCredentials(
 189                type=enums.CredentialType(int(creds["credentialType"])),
 190                display_name=creds["credentialDisplayName"],
 191                is_public=creds["isPublic"],
 192                self_as_string=creds.get("credentialAsString", undefined.UNDEFINED),
 193            )
 194            for creds in payload
 195        ]
 196
 197    def deserialize_user_themes(
 198        self, payload: typedefs.JSONArray
 199    ) -> collections.Sequence[user.UserThemes]:
 200        return [
 201            user.UserThemes(
 202                id=int(entry["userThemeId"]),
 203                name=entry["userThemeName"]
 204                if "userThemeName" in entry
 205                else undefined.UNDEFINED,
 206                description=entry["userThemeDescription"]
 207                if "userThemeDescription" in entry
 208                else undefined.UNDEFINED,
 209            )
 210            for entry in payload
 211        ]
 212
 213    def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan:
 214
 215        # This is kinda redundant
 216        data = payload
 217
 218        # This is always outside the details.
 219        current_user_map: typing.Optional[
 220            collections.Mapping[str, clans.ClanMember]
 221        ] = None
 222        if raw_current_user_map := payload.get("currentUserMemberMap"):
 223            current_user_map = {
 224                membership_type: self.deserialize_clan_member(membership)
 225                for membership_type, membership in raw_current_user_map.items()
 226            }
 227
 228        try:
 229            data = payload["detail"]
 230        except KeyError:
 231            pass
 232
 233        id = data["groupId"]
 234        name = data["name"]
 235        created_at = data["creationDate"]
 236        member_count = data["memberCount"]
 237        about = data["about"]
 238        motto = data["motto"]
 239        is_public = data["isPublic"]
 240        banner = assets.Image(str(data["bannerPath"]))
 241        avatar = assets.Image(str(data["avatarPath"]))
 242        tags = data["tags"]
 243        type = data["groupType"]
 244
 245        features = data["features"]
 246        features_obj = clans.ClanFeatures(
 247            max_members=features["maximumMembers"],
 248            max_membership_types=features["maximumMembershipsOfGroupType"],
 249            capabilities=features["capabilities"],
 250            membership_types=features["membershipTypes"],
 251            invite_permissions=features["invitePermissionOverride"],
 252            update_banner_permissions=features["updateBannerPermissionOverride"],
 253            update_culture_permissions=features["updateCulturePermissionOverride"],
 254            join_level=features["joinLevel"],
 255        )
 256
 257        information: typedefs.JSONObject = data["clanInfo"]
 258        progression: collections.Mapping[int, progressions.Progression] = {
 259            int(prog_hash): self.deserialize_progressions(prog)
 260            for prog_hash, prog in information["d2ClanProgressions"].items()
 261        }
 262
 263        founder: typedefs.NoneOr[clans.ClanMember] = None
 264        if raw_founder := payload.get("founder"):
 265            founder = self.deserialize_clan_member(raw_founder)
 266
 267        return clans.Clan(
 268            net=self._net,
 269            id=int(id),
 270            name=name,
 271            type=enums.GroupType(type),
 272            created_at=time.clean_date(created_at),
 273            member_count=member_count,
 274            motto=motto,
 275            about=about,
 276            is_public=is_public,
 277            banner=banner,
 278            avatar=avatar,
 279            tags=tags,
 280            features=features_obj,
 281            owner=founder,
 282            progressions=progression,
 283            call_sign=information["clanCallsign"],
 284            banner_data=information["clanBannerData"],
 285            chat_security=data["chatSecurity"],
 286            conversation_id=int(data["conversationId"]),
 287            allow_chat=data["allowChat"],
 288            theme=data["theme"],
 289            current_user_membership=current_user_map,
 290        )
 291
 292    def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember:
 293        destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"])
 294        return clans.ClanMember(
 295            net=self._net,
 296            last_seen_name=destiny_user.last_seen_name,
 297            id=destiny_user.id,
 298            name=destiny_user.name,
 299            icon=destiny_user.icon,
 300            last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])),
 301            group_id=int(data["groupId"]),
 302            joined_at=time.clean_date(data["joinDate"]),
 303            types=destiny_user.types,
 304            is_public=destiny_user.is_public,
 305            type=destiny_user.type,
 306            code=destiny_user.code,
 307            is_online=data["isOnline"],
 308            crossave_override=destiny_user.crossave_override,
 309            bungie=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"])
 310            if "bungieNetUserInfo" in data
 311            else None,
 312            member_type=enums.ClanMemberType(int(data["memberType"])),
 313        )
 314
 315    def deserialize_clan_members(
 316        self, data: typedefs.JSONObject, /
 317    ) -> iterators.Iterator[clans.ClanMember]:
 318        return iterators.Iterator(
 319            [self.deserialize_clan_member(member) for member in data["results"]]
 320        )
 321
 322    def deserialize_group_member(
 323        self, payload: typedefs.JSONObject
 324    ) -> clans.GroupMember:
 325        member = payload["member"]
 326        return clans.GroupMember(
 327            net=self._net,
 328            join_date=time.clean_date(member["joinDate"]),
 329            group_id=int(member["groupId"]),
 330            member_type=enums.ClanMemberType(member["memberType"]),
 331            is_online=member["isOnline"],
 332            last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])),
 333            inactive_memberships=payload.get("areAllMembershipsInactive", None),
 334            member=self.deserialize_destiny_membership(member["destinyUserInfo"]),
 335            group=self.deserialize_clan(payload["group"]),
 336        )
 337
 338    def _deserialize_clan_conversation(
 339        self, payload: typedefs.JSONObject
 340    ) -> clans.ClanConversation:
 341        return clans.ClanConversation(
 342            net=self._net,
 343            id=int(payload["conversationId"]),
 344            group_id=int(payload["groupId"]),
 345            name=(
 346                payload["chatName"]
 347                if not typedefs.is_unknown(payload["chatName"])
 348                else undefined.UNDEFINED
 349            ),
 350            chat_enabled=payload["chatEnabled"],
 351            security=payload["chatSecurity"],
 352        )
 353
 354    def deserialize_clan_conversations(
 355        self, payload: typedefs.JSONArray
 356    ) -> collections.Sequence[clans.ClanConversation]:
 357        return [self._deserialize_clan_conversation(conv) for conv in payload]
 358
 359    def deserialize_app_owner(
 360        self, payload: typedefs.JSONObject
 361    ) -> application.ApplicationOwner:
 362        return application.ApplicationOwner(
 363            net=self._net,
 364            name=payload.get("bungieGlobalDisplayName", undefined.UNDEFINED),
 365            id=int(payload["membershipId"]),
 366            type=enums.MembershipType(payload["membershipType"]),
 367            icon=assets.Image(str(payload["iconPath"])),
 368            is_public=payload["isPublic"],
 369            code=payload.get("bungieGlobalDisplayNameCode", None),
 370        )
 371
 372    def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application:
 373        return application.Application(
 374            id=int(payload["applicationId"]),
 375            name=payload["name"],
 376            link=payload["link"],
 377            status=payload["status"],
 378            redirect_url=payload.get("redirectUrl", None),
 379            created_at=time.clean_date(str(payload["creationDate"])),
 380            published_at=time.clean_date(str(payload["firstPublished"])),
 381            owner=self.deserialize_app_owner(payload["team"][0]["user"]),  # type: ignore
 382            scope=payload.get("scope", undefined.UNDEFINED),
 383        )
 384
 385    def _set_character_attrs(self, payload: typedefs.JSONObject) -> character.Character:
 386        total_time = time.format_played(int(payload["minutesPlayedTotal"]), suffix=True)
 387        return character.Character(
 388            net=self._net,
 389            id=int(payload["characterId"]),
 390            gender=enums.Gender(payload["genderType"]),
 391            race=enums.Race(payload["raceType"]),
 392            class_type=enums.Class(payload["classType"]),
 393            emblem=assets.Image(str(payload["emblemBackgroundPath"])),
 394            emblem_icon=assets.Image(str(payload["emblemPath"])),
 395            emblem_hash=int(payload["emblemHash"]),
 396            last_played=time.clean_date(payload["dateLastPlayed"]),
 397            total_played_time=total_time,
 398            member_id=int(payload["membershipId"]),
 399            member_type=enums.MembershipType(payload["membershipType"]),
 400            level=payload["baseCharacterLevel"],
 401            title_hash=payload.get("titleRecordHash", None),
 402            light=payload["light"],
 403            stats={enums.Stat(int(k)): v for k, v in payload["stats"].items()},
 404        )
 405
 406    def deserialize_profile(
 407        self, payload: typedefs.JSONObject, /
 408    ) -> typing.Optional[profile.Profile]:
 409        if (raw_profile := payload.get("data")) is None:
 410            return None
 411
 412        payload = raw_profile
 413        id = int(payload["userInfo"]["membershipId"])
 414        name = payload["userInfo"]["displayName"]
 415        is_public = payload["userInfo"]["isPublic"]
 416        type = enums.MembershipType(payload["userInfo"]["membershipType"])
 417        last_played = time.clean_date(str(payload["dateLastPlayed"]))
 418        character_ids = [int(cid) for cid in payload["characterIds"]]
 419        power_cap = payload["currentSeasonRewardPowerCap"]
 420
 421        return profile.Profile(
 422            id=int(id),
 423            name=name,
 424            is_public=is_public,
 425            type=type,
 426            last_played=last_played,
 427            character_ids=character_ids,
 428            power_cap=power_cap,
 429            net=self._net,
 430        )
 431
 432    def deserialize_profile_item(
 433        self, payload: typedefs.JSONObject
 434    ) -> profile.ProfileItemImpl:
 435
 436        instance_id: typing.Optional[int] = None
 437        if raw_instance_id := payload.get("itemInstanceId"):
 438            instance_id = int(raw_instance_id)
 439
 440        version_number: typing.Optional[int] = None
 441        if raw_version := payload.get("versionNumber"):
 442            version_number = int(raw_version)
 443
 444        transfer_status = enums.TransferStatus(payload["transferStatus"])
 445
 446        return profile.ProfileItemImpl(
 447            net=self._net,
 448            hash=payload["itemHash"],
 449            quantity=payload["quantity"],
 450            bind_status=enums.ItemBindStatus(payload["bindStatus"]),
 451            location=enums.ItemLocation(payload["location"]),
 452            bucket=payload["bucketHash"],
 453            transfer_status=transfer_status,
 454            lockable=payload["lockable"],
 455            state=enums.ItemState(payload["state"]),
 456            dismantel_permissions=payload["dismantlePermission"],
 457            is_wrapper=payload["isWrapper"],
 458            instance_id=instance_id,
 459            version_number=version_number,
 460            ornament_id=payload.get("overrideStyleItemHash"),
 461        )
 462
 463    def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective:
 464        return records.Objective(
 465            net=self._net,
 466            hash=payload["objectiveHash"],
 467            visible=payload["visible"],
 468            complete=payload["complete"],
 469            completion_value=payload["completionValue"],
 470            progress=payload.get("progress"),
 471            destination_hash=payload.get("destinationHash"),
 472            activity_hash=payload.get("activityHash"),
 473        )
 474
 475    def deserialize_records(
 476        self,
 477        payload: typedefs.JSONObject,
 478        scores: typing.Optional[records.RecordScores] = None,
 479        **nodes: int,
 480    ) -> records.Record:
 481        objectives: typing.Optional[list[records.Objective]] = None
 482        interval_objectives: typing.Optional[list[records.Objective]] = None
 483        record_state: typedefs.IntAnd[records.RecordState]
 484
 485        record_state = records.RecordState(payload["state"])
 486
 487        if raw_objs := payload.get("objectives"):
 488            objectives = [self.deserialize_objectives(obj) for obj in raw_objs]
 489
 490        if raw_interval_objs := payload.get("intervalObjectives"):
 491            interval_objectives = [
 492                self.deserialize_objectives(obj) for obj in raw_interval_objs
 493            ]
 494
 495        return records.Record(
 496            scores=scores,
 497            categories_node_hash=nodes.get("categories_hash", undefined.UNDEFINED),
 498            seals_node_hash=nodes.get("seals_hash", undefined.UNDEFINED),
 499            state=record_state,
 500            objectives=objectives,
 501            interval_objectives=interval_objectives,
 502            redeemed_count=payload.get("intervalsRedeemedCount", 0),
 503            completion_times=payload.get("completedCount", None),
 504            reward_visibility=payload.get("rewardVisibilty", None),
 505        )
 506
 507    def deserialize_character_records(
 508        self,
 509        payload: typedefs.JSONObject,
 510        scores: typing.Optional[records.RecordScores] = None,
 511        record_hashes: typing.Optional[list[int]] = None,
 512    ) -> records.CharacterRecord:
 513
 514        record = self.deserialize_records(payload, scores)
 515        return records.CharacterRecord(
 516            scores=scores,
 517            categories_node_hash=record.categories_node_hash,
 518            seals_node_hash=record.seals_node_hash,
 519            state=record.state,
 520            objectives=record.objectives,
 521            interval_objectives=record.interval_objectives,
 522            redeemed_count=payload.get("intervalsRedeemedCount", 0),
 523            completion_times=payload.get("completedCount"),
 524            reward_visibility=payload.get("rewardVisibilty"),
 525            record_hashes=record_hashes or [],
 526        )
 527
 528    def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye:
 529        return character.Dye(
 530            channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"]
 531        )
 532
 533    def deserialize_character_customization(
 534        self, payload: typedefs.JSONObject
 535    ) -> character.CustomizationOptions:
 536        return character.CustomizationOptions(
 537            personality=payload["personality"],
 538            face=payload["face"],
 539            skin_color=payload["skinColor"],
 540            lip_color=payload["lipColor"],
 541            eye_color=payload["eyeColor"],
 542            hair_colors=payload.get("hairColors", []),
 543            feature_colors=payload.get("featureColors", []),
 544            decal_color=payload["decalColor"],
 545            wear_helmet=payload["wearHelmet"],
 546            hair_index=payload["hairIndex"],
 547            feature_index=payload["featureIndex"],
 548            decal_index=payload["decalIndex"],
 549        )
 550
 551    def deserialize_character_minimal_equipments(
 552        self, payload: typedefs.JSONObject
 553    ) -> character.MinimalEquipments:
 554        dyes = None
 555        if raw_dyes := payload.get("dyes"):
 556            if raw_dyes:
 557                dyes = [self.deserialize_character_dye(dye) for dye in raw_dyes]
 558        return character.MinimalEquipments(
 559            net=self._net, item_hash=payload["itemHash"], dyes=dyes
 560        )
 561
 562    def deserialize_character_render_data(
 563        self, payload: typedefs.JSONObject, /
 564    ) -> character.RenderedData:
 565        return character.RenderedData(
 566            net=self._net,
 567            customization=self.deserialize_character_customization(
 568                payload["customization"]
 569            ),
 570            custom_dyes=[
 571                self.deserialize_character_dye(dye)
 572                for dye in payload["customDyes"]
 573                if dye
 574            ],
 575            equipment=[
 576                self.deserialize_character_minimal_equipments(equipment)
 577                for equipment in payload["peerView"]["equipment"]
 578            ],
 579        )
 580
 581    def deserialize_available_activity(
 582        self, payload: typedefs.JSONObject
 583    ) -> activity.AvailableActivity:
 584        return activity.AvailableActivity(
 585            hash=payload["activityHash"],
 586            is_new=payload["isNew"],
 587            is_completed=payload["isCompleted"],
 588            is_visible=payload["isVisible"],
 589            display_level=payload.get("displayLevel"),
 590            recommended_light=payload.get("recommendedLight"),
 591            difficulty=activity.Difficulty(payload["difficultyTier"]),
 592            can_join=payload["canJoin"],
 593            can_lead=payload["canLead"],
 594        )
 595
 596    def deserialize_character_activity(
 597        self, payload: typedefs.JSONObject
 598    ) -> activity.CharacterActivity:
 599        current_mode: typing.Optional[enums.GameMode] = None
 600        if raw_current_mode := payload.get("currentActivityModeType"):
 601            current_mode = enums.GameMode(raw_current_mode)
 602
 603        current_mode_types: typing.Optional[collections.Sequence[enums.GameMode]] = None
 604        if raw_current_modes := payload.get("currentActivityModeTypes"):
 605            current_mode_types = [enums.GameMode(type_) for type_ in raw_current_modes]
 606
 607        return activity.CharacterActivity(
 608            date_started=time.clean_date(payload["dateActivityStarted"]),
 609            current_hash=payload["currentActivityHash"],
 610            current_mode_hash=payload["currentActivityModeHash"],
 611            current_mode=current_mode,
 612            current_mode_hashes=payload.get("currentActivityModeHashes"),
 613            current_mode_types=current_mode_types,
 614            current_playlist_hash=payload.get("currentPlaylistActivityHash"),
 615            last_story_hash=payload["lastCompletedStoryHash"],
 616            available_activities=[
 617                self.deserialize_available_activity(activity_)
 618                for activity_ in payload["availableActivities"]
 619            ],
 620        )
 621
 622    def deserialize_profile_items(
 623        self, payload: typedefs.JSONObject, /
 624    ) -> list[profile.ProfileItemImpl]:
 625        return [self.deserialize_profile_item(item) for item in payload["items"]]
 626
 627    def _deserialize_node(self, payload: typedefs.JSONObject) -> records.Node:
 628        return records.Node(
 629            state=int(payload["state"]),
 630            objective=self.deserialize_objectives(payload["objective"])
 631            if "objective" in payload
 632            else None,
 633            progress_value=int(payload["progressValue"]),
 634            completion_value=int(payload["completionValue"]),
 635            record_category_score=int(payload["recordCategoryScore"])
 636            if "recordCategoryScore" in payload
 637            else None,
 638        )
 639
 640    @staticmethod
 641    def _deserialize_collectible(payload: typedefs.JSONObject) -> items.Collectible:
 642        recent_collectibles: typing.Optional[collections.Collection[int]] = None
 643        if raw_recent_collectibles := payload.get("recentCollectibleHashes"):
 644            recent_collectibles = [
 645                int(item_hash) for item_hash in raw_recent_collectibles
 646            ]
 647
 648        collectibles: dict[int, int] = {}
 649        for item_hash, mapping in payload["collectibles"].items():
 650            collectibles[int(item_hash)] = int(mapping["state"])
 651
 652        return items.Collectible(
 653            recent_collectibles=recent_collectibles,
 654            collectibles=collectibles,
 655            collection_categorie_hash=int(payload["collectionCategoriesRootNodeHash"]),
 656            collection_badges_hash=int(payload["collectionBadgesRootNodeHash"]),
 657        )
 658
 659    @staticmethod
 660    def _deserialize_currencies(
 661        payload: typedefs.JSONObject,
 662    ) -> collections.Sequence[items.Currency]:
 663        return [
 664            items.Currency(hash=int(item_hash), amount=int(amount))
 665            for item_hash, amount in payload["itemQuantities"].items()
 666        ]
 667
 668    def deserialize_progressions(
 669        self, payload: typedefs.JSONObject
 670    ) -> progressions.Progression:
 671        return progressions.Progression(
 672            hash=int(payload["progressionHash"]),
 673            level=int(payload["level"]),
 674            cap=int(payload["levelCap"]),
 675            daily_limit=int(payload["dailyLimit"]),
 676            weekly_limit=int(payload["weeklyLimit"]),
 677            current_progress=int(payload["currentProgress"]),
 678            daily_progress=int(payload["dailyProgress"]),
 679            needed=int(payload["progressToNextLevel"]),
 680            next_level=int(payload["nextLevelAt"]),
 681        )
 682
 683    def _deserialize_factions(
 684        self, payload: typedefs.JSONObject
 685    ) -> progressions.Factions:
 686        progs = self.deserialize_progressions(payload)
 687        return progressions.Factions(
 688            hash=progs.hash,
 689            level=progs.level,
 690            cap=progs.cap,
 691            daily_limit=progs.daily_limit,
 692            weekly_limit=progs.weekly_limit,
 693            current_progress=progs.current_progress,
 694            daily_progress=progs.daily_progress,
 695            needed=progs.needed,
 696            next_level=progs.next_level,
 697            faction_hash=payload["factionHash"],
 698            faction_vendor_hash=payload["factionVendorIndex"],
 699        )
 700
 701    def _deserialize_milestone_available_quest(
 702        self, payload: typedefs.JSONObject
 703    ) -> milestones.MilestoneQuest:
 704        return milestones.MilestoneQuest(
 705            item_hash=payload["questItemHash"],
 706            status=self._deserialize_milestone_quest_status(payload["status"]),
 707        )
 708
 709    def _deserialize_milestone_activity(
 710        self, payload: typedefs.JSONObject
 711    ) -> milestones.MilestoneActivity:
 712
 713        phases: typing.Optional[
 714            collections.Sequence[milestones.MilestoneActivityPhase]
 715        ] = None
 716        if raw_phases := payload.get("phases"):
 717            phases = [
 718                milestones.MilestoneActivityPhase(
 719                    is_completed=obj["complete"], hash=obj["phaseHash"]
 720                )
 721                for obj in raw_phases
 722            ]
 723
 724        return milestones.MilestoneActivity(
 725            hash=payload["activityHash"],
 726            challenges=[
 727                self.deserialize_objectives(obj["objective"])
 728                for obj in payload["challenges"]
 729            ],
 730            modifier_hashes=payload.get("modifierHashes"),
 731            boolean_options=payload.get("booleanActivityOptions"),
 732            phases=phases,
 733        )
 734
 735    def _deserialize_milestone_quest_status(
 736        self, payload: typedefs.JSONObject
 737    ) -> milestones.QuestStatus:
 738        return milestones.QuestStatus(
 739            net=self._net,
 740            quest_hash=payload["questHash"],
 741            step_hash=payload["stepHash"],
 742            step_objectives=[
 743                self.deserialize_objectives(objective)
 744                for objective in payload["stepObjectives"]
 745            ],
 746            is_tracked=payload["tracked"],
 747            is_completed=payload["completed"],
 748            started=payload["started"],
 749            item_instance_id=payload["itemInstanceId"],
 750            vendor_hash=payload.get("vendorHash"),
 751            is_redeemed=payload["redeemed"],
 752        )
 753
 754    def _deserialize_milestone_rewards(
 755        self, payload: typedefs.JSONObject
 756    ) -> milestones.MilestoneReward:
 757        return milestones.MilestoneReward(
 758            category_hash=payload["rewardCategoryHash"],
 759            entries=[
 760                milestones.MilestoneRewardEntry(
 761                    entry_hash=entry["rewardEntryHash"],
 762                    is_earned=entry["earned"],
 763                    is_redeemed=entry["redeemed"],
 764                )
 765                for entry in payload["entries"]
 766            ],
 767        )
 768
 769    def deserialize_milestone(
 770        self, payload: typedefs.JSONObject
 771    ) -> milestones.Milestone:
 772        start_date: typing.Optional[datetime.datetime] = None
 773        if raw_start_date := payload.get("startDate"):
 774            start_date = time.clean_date(raw_start_date)
 775
 776        end_date: typing.Optional[datetime.datetime] = None
 777        if raw_end_date := payload.get("endDate"):
 778            end_date = time.clean_date(raw_end_date)
 779
 780        rewards: typing.Optional[
 781            collections.Collection[milestones.MilestoneReward]
 782        ] = None
 783        if raw_rewards := payload.get("rewards"):
 784            rewards = [
 785                self._deserialize_milestone_rewards(reward) for reward in raw_rewards
 786            ]
 787
 788        activities: typing.Optional[
 789            collections.Sequence[milestones.MilestoneActivity]
 790        ] = None
 791        if raw_activities := payload.get("activities"):
 792            activities = [
 793                self._deserialize_milestone_activity(active)
 794                for active in raw_activities
 795            ]
 796
 797        quests: typing.Optional[collections.Sequence[milestones.MilestoneQuest]] = None
 798        if raw_quests := payload.get("availableQuests"):
 799            quests = [
 800                self._deserialize_milestone_available_quest(quest)
 801                for quest in raw_quests
 802            ]
 803
 804        vendors: typing.Optional[
 805            collections.Sequence[milestones.MilestoneVendor]
 806        ] = None
 807        if raw_vendors := payload.get("vendors"):
 808            vendors = [
 809                milestones.MilestoneVendor(
 810                    vendor_hash=vendor["vendorHash"],
 811                    preview_itemhash=vendor.get("previewItemHash"),
 812                )
 813                for vendor in raw_vendors
 814            ]
 815
 816        return milestones.Milestone(
 817            hash=payload["milestoneHash"],
 818            start_date=start_date,
 819            end_date=end_date,
 820            order=payload["order"],
 821            rewards=rewards,
 822            available_quests=quests,
 823            activities=activities,
 824            vendors=vendors,
 825        )
 826
 827    def _deserialize_artifact_tiers(
 828        self, payload: typedefs.JSONObject
 829    ) -> season.ArtifactTier:
 830        return season.ArtifactTier(
 831            hash=payload["tierHash"],
 832            is_unlocked=payload["isUnlocked"],
 833            points_to_unlock=payload["pointsToUnlock"],
 834            items=[
 835                season.ArtifactTierItem(
 836                    hash=item["itemHash"], is_active=item["isActive"]
 837                )
 838                for item in payload["items"]
 839            ],
 840        )
 841
 842    def deserialize_characters(
 843        self, payload: typedefs.JSONObject
 844    ) -> collections.Mapping[int, character.Character]:
 845        return {
 846            int(char_id): self._set_character_attrs(char)
 847            for char_id, char in payload["data"].items()
 848        }
 849
 850    def deserialize_character(
 851        self, payload: typedefs.JSONObject
 852    ) -> character.Character:
 853        return self._set_character_attrs(payload)
 854
 855    def deserialize_character_equipments(
 856        self, payload: typedefs.JSONObject
 857    ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]:
 858        return {
 859            int(char_id): self.deserialize_profile_items(item)
 860            for char_id, item in payload["data"].items()
 861        }
 862
 863    def deserialize_character_activities(
 864        self, payload: typedefs.JSONObject
 865    ) -> collections.Mapping[int, activity.CharacterActivity]:
 866        return {
 867            int(char_id): self.deserialize_character_activity(data)
 868            for char_id, data in payload["data"].items()
 869        }
 870
 871    def deserialize_characters_render_data(
 872        self, payload: typedefs.JSONObject
 873    ) -> collections.Mapping[int, character.RenderedData]:
 874        return {
 875            int(char_id): self.deserialize_character_render_data(data)
 876            for char_id, data in payload["data"].items()
 877        }
 878
 879    def deserialize_character_progressions(
 880        self, payload: typedefs.JSONObject
 881    ) -> character.CharacterProgression:
 882        progressions_ = {
 883            int(prog_id): self.deserialize_progressions(prog)
 884            for prog_id, prog in payload["progressions"].items()
 885        }
 886
 887        factions = {
 888            int(faction_id): self._deserialize_factions(faction)
 889            for faction_id, faction in payload["factions"].items()
 890        }
 891
 892        milestones_ = {
 893            int(milestone_hash): self.deserialize_milestone(milestone)
 894            for milestone_hash, milestone in payload["milestones"].items()
 895        }
 896
 897        uninstanced_item_objectives = {
 898            int(item_hash): [self.deserialize_objectives(ins) for ins in obj]
 899            for item_hash, obj in payload["uninstancedItemObjectives"].items()
 900        }
 901
 902        artifact = payload["seasonalArtifact"]
 903        seasonal_artifact = season.CharacterScopedArtifact(
 904            hash=artifact["artifactHash"],
 905            points_used=artifact["pointsUsed"],
 906            reset_count=artifact["resetCount"],
 907            tiers=[
 908                self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"]
 909            ],
 910        )
 911        checklists = payload["checklists"]
 912
 913        return character.CharacterProgression(
 914            progressions=progressions_,
 915            factions=factions,
 916            checklists=checklists,
 917            milestones=milestones_,
 918            seasonal_artifact=seasonal_artifact,
 919            uninstanced_item_objectives=uninstanced_item_objectives,
 920        )
 921
 922    def deserialize_character_progressions_mapping(
 923        self, payload: typedefs.JSONObject
 924    ) -> collections.Mapping[int, character.CharacterProgression]:
 925        character_progressions: collections.Mapping[
 926            int, character.CharacterProgression
 927        ] = {}
 928        for char_id, data in payload["data"].items():
 929            # A little hack to stop mypy complaining about Mapping <-> dict
 930            character_progressions[int(char_id)] = self.deserialize_character_progressions(data)  # type: ignore[index]
 931        return character_progressions
 932
 933    def deserialize_characters_records(
 934        self,
 935        payload: typedefs.JSONObject,
 936    ) -> collections.Mapping[int, records.CharacterRecord]:
 937
 938        return {
 939            int(rec_id): self.deserialize_character_records(
 940                rec, record_hashes=payload.get("featuredRecordHashes")
 941            )
 942            for rec_id, rec in payload["records"].items()
 943        }
 944
 945    def deserialize_profile_records(
 946        self, payload: typedefs.JSONObject
 947    ) -> collections.Mapping[int, records.Record]:
 948        raw_profile_records = payload["data"]
 949        scores = records.RecordScores(
 950            current_score=raw_profile_records["score"],
 951            legacy_score=raw_profile_records["legacyScore"],
 952            lifetime_score=raw_profile_records["lifetimeScore"],
 953        )
 954        return {
 955            int(record_id): self.deserialize_records(
 956                record,
 957                scores,
 958                categories_hash=raw_profile_records["recordCategoriesRootNodeHash"],
 959                seals_hash=raw_profile_records["recordSealsRootNodeHash"],
 960            )
 961            for record_id, record in raw_profile_records["records"].items()
 962        }
 963
 964    def _deserialize_craftable_socket_plug(
 965        self, payload: typedefs.JSONObject
 966    ) -> items.CraftableSocketPlug:
 967        return items.CraftableSocketPlug(
 968            item_hash=int(payload["plugItemHash"]),
 969            failed_requirement_indexes=payload.get("failedRequirementIndexes", []),
 970        )
 971
 972    def _deserialize_craftable_socket(
 973        self, payload: typedefs.JSONObject
 974    ) -> items.CraftableSocket:
 975
 976        plugs: list[items.CraftableSocketPlug] = []
 977        if raw_plug := payload.get("plug"):
 978            plugs.extend(
 979                self._deserialize_craftable_socket_plug(plug) for plug in raw_plug
 980            )
 981
 982        return items.CraftableSocket(
 983            plug_set_hash=int(payload["plugSetHash"]), plugs=plugs
 984        )
 985
 986    def _deserialize_craftable_item(
 987        self, payload: typedefs.JSONObject
 988    ) -> items.CraftableItem:
 989
 990        return items.CraftableItem(
 991            is_visible=payload["visible"],
 992            failed_requirement_indexes=payload.get("failedRequirementIndexes", []),
 993            sockets=[
 994                self._deserialize_craftable_socket(socket)
 995                for socket in payload["sockets"]
 996            ],
 997        )
 998
 999    def deserialize_craftables_component(
1000        self, payload: typedefs.JSONObject
1001    ) -> components.CraftablesComponent:
1002        return components.CraftablesComponent(
1003            net=self._net,
1004            craftables={
1005                int(item_id): self._deserialize_craftable_item(item)
1006                for item_id, item in payload["craftables"].items()
1007                if item is not None
1008            },
1009            crafting_root_node_hash=payload["craftingRootNodeHash"],
1010        )
1011
1012    def deserialize_components(  # noqa: C901 Too complex.
1013        self, payload: typedefs.JSONObject
1014    ) -> components.Component:
1015
1016        profile_: typing.Optional[profile.Profile] = None
1017        if raw_profile := payload.get("profile"):
1018            profile_ = self.deserialize_profile(raw_profile)
1019
1020        profile_progression: typing.Optional[profile.ProfileProgression] = None
1021        if raw_profile_progression := payload.get("profileProgression"):
1022            profile_progression = self.deserialize_profile_progression(
1023                raw_profile_progression
1024            )
1025
1026        profile_currencies: typing.Optional[
1027            collections.Sequence[profile.ProfileItemImpl]
1028        ] = None
1029        if raw_profile_currencies := payload.get("profileCurrencies"):
1030            if "data" in raw_profile_currencies:
1031                profile_currencies = self.deserialize_profile_items(
1032                    raw_profile_currencies["data"]
1033                )
1034
1035        profile_inventories: typing.Optional[
1036            collections.Sequence[profile.ProfileItemImpl]
1037        ] = None
1038        if raw_profile_inventories := payload.get("profileInventory"):
1039            if "data" in raw_profile_inventories:
1040                profile_inventories = self.deserialize_profile_items(
1041                    raw_profile_inventories["data"]
1042                )
1043
1044        profile_records: typing.Optional[
1045            collections.Mapping[int, records.Record]
1046        ] = None
1047
1048        if raw_profile_records_ := payload.get("profileRecords"):
1049            profile_records = self.deserialize_profile_records(raw_profile_records_)
1050
1051        characters: typing.Optional[typing.Mapping[int, character.Character]] = None
1052        if raw_characters := payload.get("characters"):
1053            characters = self.deserialize_characters(raw_characters)
1054
1055        character_records: typing.Optional[
1056            collections.Mapping[int, records.CharacterRecord]
1057        ] = None
1058
1059        if raw_character_records := payload.get("characterRecords"):
1060            # Had to do it in two steps..
1061            to_update: typedefs.JSONObject = {}
1062            for _, data in raw_character_records["data"].items():
1063                for record_id, record in data.items():
1064                    to_update[record_id] = record
1065
1066            character_records = {
1067                int(rec_id): self.deserialize_character_records(
1068                    rec, record_hashes=to_update.get("featuredRecordHashes")
1069                )
1070                for rec_id, rec in to_update["records"].items()
1071            }
1072
1073        character_equipments: typing.Optional[
1074            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1075        ] = None
1076        if raw_character_equips := payload.get("characterEquipment"):
1077            character_equipments = self.deserialize_character_equipments(
1078                raw_character_equips
1079            )
1080
1081        character_inventories: typing.Optional[
1082            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1083        ] = None
1084        if raw_character_inventories := payload.get("characterInventories"):
1085            if "data" in raw_character_inventories:
1086                character_inventories = self.deserialize_character_equipments(
1087                    raw_character_inventories
1088                )
1089
1090        character_activities: typing.Optional[
1091            collections.Mapping[int, activity.CharacterActivity]
1092        ] = None
1093        if raw_char_acts := payload.get("characterActivities"):
1094            character_activities = self.deserialize_character_activities(raw_char_acts)
1095
1096        character_render_data: typing.Optional[
1097            collections.Mapping[int, character.RenderedData]
1098        ] = None
1099        if raw_character_render_data := payload.get("characterRenderData"):
1100            character_render_data = self.deserialize_characters_render_data(
1101                raw_character_render_data
1102            )
1103
1104        character_progressions: typing.Optional[
1105            collections.Mapping[int, character.CharacterProgression]
1106        ] = None
1107
1108        if raw_character_progressions := payload.get("characterProgressions"):
1109            character_progressions = self.deserialize_character_progressions_mapping(
1110                raw_character_progressions
1111            )
1112
1113        profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None
1114        if raw_profile_string_vars := payload.get("profileStringVariables"):
1115            profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"]
1116
1117        character_string_vars: typing.Optional[
1118            collections.Mapping[int, collections.Mapping[int, int]]
1119        ] = None
1120        if raw_character_string_vars := payload.get("characterStringVariables"):
1121            character_string_vars = {
1122                int(char_id): data["integerValuesByHash"]
1123                for char_id, data in raw_character_string_vars["data"].items()
1124            }
1125
1126        metrics: typing.Optional[
1127            collections.Sequence[
1128                collections.Mapping[
1129                    int, tuple[bool, typing.Optional[records.Objective]]
1130                ]
1131            ]
1132        ] = None
1133        root_node_hash: typing.Optional[int] = None
1134
1135        if raw_metrics := payload.get("metrics"):
1136            root_node_hash = raw_metrics["data"]["metricsRootNodeHash"]
1137            metrics = [
1138                {
1139                    int(metrics_hash): (
1140                        data["invisible"],
1141                        self.deserialize_objectives(data["objectiveProgress"])
1142                        if "objectiveProgress" in data
1143                        else None,
1144                    )
1145                    for metrics_hash, data in raw_metrics["data"]["metrics"].items()
1146                }
1147            ]
1148        transitory: typing.Optional[fireteams.FireteamParty] = None
1149        if raw_transitory := payload.get("profileTransitoryData"):
1150            if "data" in raw_transitory:
1151                transitory = self.deserialize_fireteam_party(raw_transitory["data"])
1152
1153        item_components: typing.Optional[components.ItemsComponent] = None
1154        if raw_item_components := payload.get("itemComponents"):
1155            item_components = self.deserialize_items_component(raw_item_components)
1156
1157        profile_plugsets: typing.Optional[
1158            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1159        ] = None
1160
1161        if raw_profile_plugs := payload.get("profilePlugSets"):
1162            profile_plugsets = {
1163                int(index): [self.deserialize_plug_item_state(state) for state in data]
1164                for index, data in raw_profile_plugs["data"]["plugs"].items()
1165            }
1166
1167        character_plugsets: typing.Optional[
1168            collections.Mapping[
1169                int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1170            ]
1171        ] = None
1172        if raw_char_plugsets := payload.get("characterPlugSets"):
1173            character_plugsets = {
1174                int(char_id): {
1175                    int(index): [
1176                        self.deserialize_plug_item_state(state) for state in data
1177                    ]
1178                    for index, data in inner["plugs"].items()
1179                }
1180                for char_id, inner in raw_char_plugsets["data"].items()
1181            }
1182
1183        character_collectibles: typing.Optional[
1184            collections.Mapping[int, items.Collectible]
1185        ] = None
1186        if raw_character_collectibles := payload.get("characterCollectibles"):
1187            character_collectibles = {
1188                int(char_id): self._deserialize_collectible(data)
1189                for char_id, data in raw_character_collectibles["data"].items()
1190            }
1191
1192        profile_collectibles: typing.Optional[items.Collectible] = None
1193        if raw_profile_collectibles := payload.get("profileCollectibles"):
1194            profile_collectibles = self._deserialize_collectible(
1195                raw_profile_collectibles["data"]
1196            )
1197
1198        profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1199        if raw_profile_nodes := payload.get("profilePresentationNodes"):
1200            profile_nodes = {
1201                int(node_hash): self._deserialize_node(node)
1202                for node_hash, node in raw_profile_nodes["data"]["nodes"].items()
1203            }
1204
1205        character_nodes: typing.Optional[
1206            collections.Mapping[int, collections.Mapping[int, records.Node]]
1207        ] = None
1208        if raw_character_nodes := payload.get("characterPresentationNodes"):
1209            character_nodes = {
1210                int(char_id): {
1211                    int(node_hash): self._deserialize_node(node)
1212                    for node_hash, node in each_character["nodes"].items()
1213                }
1214                for char_id, each_character in raw_character_nodes["data"].items()
1215            }
1216
1217        platform_silver: typing.Optional[
1218            collections.Mapping[str, profile.ProfileItemImpl]
1219        ] = None
1220        if raw_platform_silver := payload.get("platformSilver"):
1221            if "data" in raw_platform_silver:
1222                platform_silver = {
1223                    platform_name: self.deserialize_profile_item(item)
1224                    for platform_name, item in raw_platform_silver["data"][
1225                        "platformSilver"
1226                    ].items()
1227                }
1228
1229        character_currency_lookups: typing.Optional[
1230            collections.Mapping[int, collections.Sequence[items.Currency]]
1231        ] = None
1232        if raw_char_lookups := payload.get("characterCurrencyLookups"):
1233            if "data" in raw_char_lookups:
1234                character_currency_lookups = {
1235                    int(char_id): self._deserialize_currencies(currencie)
1236                    for char_id, currencie in raw_char_lookups["data"].items()
1237                }
1238
1239        character_craftables: typing.Optional[
1240            collections.Mapping[int, components.CraftablesComponent]
1241        ] = None
1242        if raw_character_craftables := payload.get("characterCraftables"):
1243
1244            if "data" in raw_character_craftables:
1245                character_craftables = {
1246                    int(char_id): self.deserialize_craftables_component(craftable)
1247                    for char_id, craftable in raw_character_craftables["data"].items()
1248                }
1249
1250        return components.Component(
1251            profiles=profile_,
1252            profile_progression=profile_progression,
1253            profile_currencies=profile_currencies,
1254            profile_inventories=profile_inventories,
1255            profile_records=profile_records,
1256            characters=characters,
1257            character_records=character_records,
1258            character_equipments=character_equipments,
1259            character_inventories=character_inventories,
1260            character_activities=character_activities,
1261            character_render_data=character_render_data,
1262            character_progressions=character_progressions,
1263            profile_string_variables=profile_string_vars,
1264            character_string_variables=character_string_vars,
1265            metrics=metrics,
1266            root_node_hash=root_node_hash,
1267            transitory=transitory,
1268            item_components=item_components,
1269            profile_plugsets=profile_plugsets,
1270            character_plugsets=character_plugsets,
1271            character_collectibles=character_collectibles,
1272            profile_collectibles=profile_collectibles,
1273            profile_nodes=profile_nodes,
1274            character_nodes=character_nodes,
1275            platform_silver=platform_silver,
1276            character_currency_lookups=character_currency_lookups,
1277            character_craftables=character_craftables,
1278        )
1279
1280    def deserialize_items_component(
1281        self, payload: typedefs.JSONObject
1282    ) -> components.ItemsComponent:
1283        instances: typing.Optional[
1284            collections.Sequence[collections.Mapping[int, items.ItemInstance]]
1285        ] = None
1286        if raw_instances := payload.get("instances"):
1287            instances = [
1288                {
1289                    int(ins_id): self.deserialize_instanced_item(item)
1290                    for ins_id, item in raw_instances["data"].items()
1291                }
1292            ]
1293
1294        render_data: typing.Optional[
1295            collections.Mapping[int, tuple[bool, dict[int, int]]]
1296        ] = None
1297        if raw_render_data := payload.get("renderData"):
1298            render_data = {
1299                int(ins_id): (data["useCustomDyes"], data["artRegions"])
1300                for ins_id, data in raw_render_data["data"].items()
1301            }
1302
1303        stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None
1304        if raw_stats := payload.get("stats"):
1305            builder: collections.Mapping[int, items.ItemStatsView] = {}
1306            for ins_id, stat in raw_stats["data"].items():
1307                for _, items_ in stat.items():
1308                    builder[int(ins_id)] = self.deserialize_item_stats_view(items_)  # type: ignore[index]
1309            stats = builder
1310
1311        sockets: typing.Optional[
1312            collections.Mapping[int, collections.Sequence[items.ItemSocket]]
1313        ] = None
1314        if raw_sockets := payload.get("sockets"):
1315            sockets = {
1316                int(ins_id): [
1317                    self.deserialize_item_socket(socket) for socket in item["sockets"]
1318                ]
1319                for ins_id, item in raw_sockets["data"].items()
1320            }
1321
1322        objeectives: typing.Optional[
1323            collections.Mapping[int, collections.Sequence[records.Objective]]
1324        ] = None
1325        if raw_objectives := payload.get("objectives"):
1326            objeectives = {
1327                int(ins_id): [self.deserialize_objectives(objective)]
1328                for ins_id, data in raw_objectives["data"].items()
1329                for objective in data["objectives"]
1330            }
1331
1332        perks: typing.Optional[
1333            collections.Mapping[int, collections.Collection[items.ItemPerk]]
1334        ] = None
1335        if raw_perks := payload.get("perks"):
1336            perks = {
1337                int(ins_id): [
1338                    self.deserialize_item_perk(perk) for perk in item["perks"]
1339                ]
1340                for ins_id, item in raw_perks["data"].items()
1341            }
1342
1343        plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None
1344        if raw_plug_states := payload.get("plugStates"):
1345            pending_states: list[items.PlugItemState] = []
1346            for _, plug in raw_plug_states["data"].items():
1347                pending_states.append(self.deserialize_plug_item_state(plug))
1348            plug_states = pending_states
1349
1350        reusable_plugs: typing.Optional[
1351            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1352        ] = None
1353        if raw_re_plugs := payload.get("reusablePlugs"):
1354            reusable_plugs = {
1355                int(ins_id): [
1356                    self.deserialize_plug_item_state(state) for state in inner
1357                ]
1358                for ins_id, plug in raw_re_plugs["data"].items()
1359                for inner in list(plug["plugs"].values())
1360            }
1361
1362        plug_objectives: typing.Optional[
1363            collections.Mapping[
1364                int, collections.Mapping[int, collections.Collection[records.Objective]]
1365            ]
1366        ] = None
1367        if raw_plug_objectives := payload.get("plugObjectives"):
1368            plug_objectives = {
1369                int(ins_id): {
1370                    int(obj_hash): [self.deserialize_objectives(obj) for obj in objs]
1371                    for obj_hash, objs in inner["objectivesPerPlug"].items()
1372                }
1373                for ins_id, inner in raw_plug_objectives["data"].items()
1374            }
1375
1376        return components.ItemsComponent(
1377            sockets=sockets,
1378            stats=stats,
1379            render_data=render_data,
1380            instances=instances,
1381            objectives=objeectives,
1382            perks=perks,
1383            plug_states=plug_states,
1384            reusable_plugs=reusable_plugs,
1385            plug_objectives=plug_objectives,
1386        )
1387
1388    def deserialize_character_component(  # type: ignore[call-arg]
1389        self, payload: typedefs.JSONObject
1390    ) -> components.CharacterComponent:
1391
1392        character_: typing.Optional[character.Character] = None
1393        if raw_singuler_character := payload.get("character"):
1394            character_ = self.deserialize_character(raw_singuler_character["data"])
1395
1396        inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1397        if raw_inventory := payload.get("inventory"):
1398            if "data" in raw_inventory:
1399                inventory = self.deserialize_profile_items(raw_inventory["data"])
1400
1401        activities: typing.Optional[activity.CharacterActivity] = None
1402        if raw_activities := payload.get("activities"):
1403            activities = self.deserialize_character_activity(raw_activities["data"])
1404
1405        equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1406        if raw_equipments := payload.get("equipment"):
1407            equipment = self.deserialize_profile_items(raw_equipments["data"])
1408
1409        progressions_: typing.Optional[character.CharacterProgression] = None
1410        if raw_progressions := payload.get("progressions"):
1411            progressions_ = self.deserialize_character_progressions(
1412                raw_progressions["data"]
1413            )
1414
1415        render_data: typing.Optional[character.RenderedData] = None
1416        if raw_render_data := payload.get("renderData"):
1417            render_data = self.deserialize_character_render_data(
1418                raw_render_data["data"]
1419            )
1420
1421        character_records: typing.Optional[
1422            collections.Mapping[int, records.CharacterRecord]
1423        ] = None
1424        if raw_char_records := payload.get("records"):
1425            character_records = self.deserialize_characters_records(
1426                raw_char_records["data"]
1427            )
1428
1429        item_components: typing.Optional[components.ItemsComponent] = None
1430        if raw_item_components := payload.get("itemComponents"):
1431            item_components = self.deserialize_items_component(raw_item_components)
1432
1433        nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1434        if raw_nodes := payload.get("presentationNodes"):
1435            nodes = {
1436                int(node_hash): self._deserialize_node(node)
1437                for node_hash, node in raw_nodes["data"]["nodes"].items()
1438            }
1439
1440        collectibles: typing.Optional[items.Collectible] = None
1441        if raw_collectibles := payload.get("collectibles"):
1442            collectibles = self._deserialize_collectible(raw_collectibles["data"])
1443
1444        currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None
1445        if raw_currencies := payload.get("currencyLookups"):
1446            if "data" in raw_currencies:
1447                currency_lookups = self._deserialize_currencies(raw_currencies)
1448
1449        return components.CharacterComponent(
1450            activities=activities,
1451            equipment=equipment,
1452            inventory=inventory,
1453            progressions=progressions_,
1454            render_data=render_data,
1455            character=character_,
1456            character_records=character_records,
1457            profile_records=None,
1458            item_components=item_components,
1459            currency_lookups=currency_lookups,
1460            collectibles=collectibles,
1461            nodes=nodes,
1462        )
1463
1464    def _set_entity_attrs(
1465        self, payload: typedefs.JSONObject, *, key: str = "displayProperties"
1466    ) -> entity.Entity:
1467
1468        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1469        description: undefined.UndefinedOr[str] = undefined.UNDEFINED
1470
1471        if properties := payload[key]:
1472            if (raw_name := properties["name"]) is not typedefs.Unknown:
1473                name = raw_name
1474
1475            if (
1476                raw_description := properties["description"]
1477            ) and not typedefs.is_unknown(raw_description):
1478                description = raw_description
1479
1480        return entity.Entity(
1481            net=self._net,
1482            hash=payload["hash"],
1483            index=payload["index"],
1484            name=name,
1485            description=description,
1486            has_icon=properties["hasIcon"],
1487            icon=assets.Image(properties["icon"] if "icon" in properties else None),
1488        )
1489
1490    def deserialize_inventory_results(
1491        self, payload: typedefs.JSONObject
1492    ) -> iterators.Iterator[entity.SearchableEntity]:
1493        suggested_words: list[str] = payload["suggestedWords"]
1494
1495        def _check_unknown(s: str) -> undefined.UndefinedOr[str]:
1496            return s if not typedefs.is_unknown(s) else undefined.UNDEFINED
1497
1498        return iterators.Iterator(
1499            [
1500                entity.SearchableEntity(
1501                    net=self._net,
1502                    hash=data["hash"],
1503                    entity_type=data["entityType"],
1504                    weight=data["weight"],
1505                    suggested_words=suggested_words,
1506                    name=data["displayProperties"]["name"],
1507                    has_icon=data["displayProperties"]["hasIcon"],
1508                    description=_check_unknown(
1509                        data["displayProperties"]["description"]
1510                    ),
1511                    icon=assets.Image(data["displayProperties"]["icon"]),
1512                )
1513                for data in payload["results"]["results"]
1514            ]
1515        )
1516
1517    def _deserialize_inventory_item_objects(
1518        self, payload: typedefs.JSONObject
1519    ) -> entity.InventoryEntityObjects:
1520        return entity.InventoryEntityObjects(
1521            action=payload.get("action"),
1522            set_data=payload.get("setData"),
1523            stats=payload.get("stats"),
1524            equipping_block=payload.get("equippingBlock"),
1525            translation_block=payload.get("translationBlock"),
1526            preview=payload.get("preview"),
1527            quality=payload.get("quality"),
1528            value=payload.get("value"),
1529            source_data=payload.get("sourceData"),
1530            objectives=payload.get("objectives"),
1531            plug=payload.get("plug"),
1532            metrics=payload.get("metrics"),
1533            gearset=payload.get("gearset"),
1534            sack=payload.get("sack"),
1535            sockets=payload.get("sockets"),
1536            summary=payload.get("summary"),
1537            talent_gird=payload.get("talentGrid"),
1538            investments_stats=payload.get("investmentStats"),
1539            perks=payload.get("perks"),
1540            animations=payload.get("animations", []),
1541            links=payload.get("links", []),
1542        )
1543
1544    def deserialize_inventory_entity(  # noqa: C901 Too complex.
1545        self, payload: typedefs.JSONObject, /
1546    ) -> entity.InventoryEntity:
1547
1548        props = self._set_entity_attrs(payload)
1549        objects = self._deserialize_inventory_item_objects(payload)
1550
1551        collectible_hash: typing.Optional[int] = None
1552        if raw_collectible_hash := payload.get("collectibleHash"):
1553            collectible_hash = int(raw_collectible_hash)
1554
1555        secondary_icon: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1556        if raw_second_icon := payload.get("secondaryIcon"):
1557            secondary_icon = assets.Image(raw_second_icon)
1558
1559        secondary_overlay: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1560        if raw_second_overlay := payload.get("secondaryOverlay"):
1561            secondary_overlay = assets.Image(raw_second_overlay)
1562
1563        secondary_special: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1564        if raw_second_special := payload.get("secondarySpecial"):
1565            secondary_special = assets.Image(raw_second_special)
1566
1567        screenshot: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1568        if raw_screenshot := payload.get("screenshot"):
1569            screenshot = assets.Image(raw_screenshot)
1570
1571        watermark_icon: typing.Optional[assets.Image] = None
1572        if raw_watermark_icon := payload.get("iconWatermark"):
1573            watermark_icon = assets.Image(raw_watermark_icon)
1574
1575        watermark_shelved: typing.Optional[assets.Image] = None
1576        if raw_watermark_shelved := payload.get("iconWatermarkShelved"):
1577            watermark_shelved = assets.Image(raw_watermark_shelved)
1578
1579        about: undefined.UndefinedOr[str] = undefined.UNDEFINED
1580        if (raw_about := payload.get("flavorText")) and not typedefs.is_unknown(
1581            raw_about
1582        ):
1583            about = raw_about
1584
1585        ui_item_style: undefined.UndefinedOr[str] = undefined.UNDEFINED
1586        if (
1587            raw_ui_style := payload.get("uiItemDisplayStyle")
1588        ) and not typedefs.is_unknown(raw_ui_style):
1589            ui_item_style = raw_ui_style
1590
1591        tier_and_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1592        if (
1593            raw_tier_and_name := payload.get("itemTypeAndTierDisplayName")
1594        ) and not typedefs.is_unknown(raw_tier_and_name):
1595            tier_and_name = raw_tier_and_name
1596
1597        type_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1598        if (
1599            raw_type_name := payload.get("itemTypeDisplayName")
1600        ) and not typedefs.is_unknown(raw_type_name):
1601            type_name = raw_type_name
1602
1603        display_source: undefined.UndefinedOr[str] = undefined.UNDEFINED
1604        if (
1605            raw_display_source := payload.get("displaySource")
1606        ) and not typedefs.is_unknown(raw_display_source):
1607            display_source = raw_display_source
1608
1609        lorehash: typing.Optional[int] = None
1610        if raw_lore_hash := payload.get("loreHash"):
1611            lorehash = int(raw_lore_hash)
1612
1613        summary_hash: typing.Optional[int] = None
1614        if raw_summary_hash := payload.get("summaryItemHash"):
1615            summary_hash = raw_summary_hash
1616
1617        breaker_type_hash: typing.Optional[int] = None
1618        if raw_breaker_type_hash := payload.get("breakerTypeHash"):
1619            breaker_type_hash = int(raw_breaker_type_hash)
1620
1621        damage_types: typing.Optional[collections.Sequence[int]] = None
1622        if raw_damage_types := payload.get("damageTypes"):
1623            damage_types = [int(type_) for type_ in raw_damage_types]
1624
1625        damagetype_hashes: typing.Optional[collections.Sequence[int]] = None
1626        if raw_damagetype_hashes := payload.get("damageTypeHashes"):
1627            damagetype_hashes = [int(type_) for type_ in raw_damagetype_hashes]
1628
1629        default_damagetype_hash: typing.Optional[int] = None
1630        if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"):
1631            default_damagetype_hash = int(raw_defaultdmg_hash)
1632
1633        emblem_objective_hash: typing.Optional[int] = None
1634        if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"):
1635            emblem_objective_hash = int(raw_emblem_obj_hash)
1636
1637        tier_type: typing.Optional[enums.TierType] = None
1638        tier: typing.Optional[enums.ItemTier] = None
1639        bucket_hash: typing.Optional[int] = None
1640        recovery_hash: typing.Optional[int] = None
1641        tier_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1642        isinstance_item: bool = False
1643        expire_tool_tip: undefined.UndefinedOr[str] = undefined.UNDEFINED
1644        expire_in_orbit_message: undefined.UndefinedOr[str] = undefined.UNDEFINED
1645        suppress_expiration: bool = False
1646        max_stack_size: typing.Optional[int] = None
1647        stack_label: undefined.UndefinedOr[str] = undefined.UNDEFINED
1648
1649        if inventory := payload.get("inventory"):
1650            tier_type = enums.TierType(int(inventory["tierType"]))
1651            tier = enums.ItemTier(int(inventory["tierTypeHash"]))
1652            bucket_hash = int(inventory["bucketTypeHash"])
1653            recovery_hash = int(inventory["recoveryBucketTypeHash"])
1654            tier_name = inventory["tierTypeName"]
1655            isinstance_item = inventory["isInstanceItem"]
1656            suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"]
1657            max_stack_size = int(inventory["maxStackSize"])
1658
1659            try:
1660                stack_label = inventory["stackUniqueLabel"]
1661            except KeyError:
1662                pass
1663
1664        return entity.InventoryEntity(
1665            net=self._net,
1666            collectible_hash=collectible_hash,
1667            name=props.name,
1668            about=about,
1669            emblem_objective_hash=emblem_objective_hash,
1670            suppress_expiration=suppress_expiration,
1671            max_stack_size=max_stack_size,
1672            stack_label=stack_label,
1673            tier=tier,
1674            tier_type=tier_type,
1675            tier_name=tier_name,
1676            bucket_hash=bucket_hash,
1677            recovery_bucket_hash=recovery_hash,
1678            isinstance_item=isinstance_item,
1679            expire_in_orbit_message=expire_in_orbit_message,
1680            expiration_tooltip=expire_tool_tip,
1681            lore_hash=lorehash,
1682            type_and_tier_name=tier_and_name,
1683            summary_hash=summary_hash,
1684            ui_display_style=ui_item_style,
1685            type_name=type_name,
1686            breaker_type_hash=breaker_type_hash,
1687            description=props.description,
1688            display_source=display_source,
1689            hash=props.hash,
1690            damage_types=damage_types,
1691            index=props.index,
1692            icon=props.icon,
1693            has_icon=props.has_icon,
1694            screenshot=screenshot,
1695            watermark_icon=watermark_icon,
1696            watermark_shelved=watermark_shelved,
1697            secondary_icon=secondary_icon,
1698            secondary_overlay=secondary_overlay,
1699            secondary_special=secondary_special,
1700            type=enums.ItemType(int(payload["itemType"])),
1701            trait_hashes=[int(id_) for id_ in payload.get("traitHashes", [])],
1702            trait_ids=[trait for trait in payload.get("traitIds", [])],
1703            category_hashes=[int(hash_) for hash_ in payload["itemCategoryHashes"]],
1704            item_class=enums.Class(int(payload["classType"])),
1705            sub_type=enums.ItemSubType(int(payload["itemSubType"])),
1706            breaker_type=int(payload["breakerType"]),
1707            default_damagetype=int(payload["defaultDamageType"]),
1708            default_damagetype_hash=default_damagetype_hash,
1709            damagetype_hashes=damagetype_hashes,
1710            tooltip_notifications=payload["tooltipNotifications"],
1711            not_transferable=payload["nonTransferrable"],
1712            allow_actions=payload["allowActions"],
1713            is_equippable=payload["equippable"],
1714            objects=objects,
1715            background_colors=payload.get("backgroundColor", {}),
1716            season_hash=payload.get("seasonHash"),
1717            has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"],
1718        )
1719
1720    def deserialize_objective_entity(
1721        self, payload: typedefs.JSONObject, /
1722    ) -> entity.ObjectiveEntity:
1723        props = self._set_entity_attrs(payload)
1724        return entity.ObjectiveEntity(
1725            net=self._net,
1726            hash=props.hash,
1727            index=props.index,
1728            description=props.description,
1729            name=props.name,
1730            has_icon=props.has_icon,
1731            icon=props.icon,
1732            unlock_value_hash=payload["unlockValueHash"],
1733            completion_value=payload["completionValue"],
1734            scope=entity.GatingScope(int(payload["scope"])),
1735            location_hash=payload["locationHash"],
1736            allowed_negative_value=payload["allowNegativeValue"],
1737            allowed_value_change=payload["allowValueChangeWhenCompleted"],
1738            counting_downward=payload["isCountingDownward"],
1739            value_style=entity.ValueUIStyle(int(payload["valueStyle"])),
1740            progress_description=payload["progressDescription"],
1741            perks=payload["perks"],
1742            stats=payload["stats"],
1743            minimum_visibility=payload["minimumVisibilityThreshold"],
1744            allow_over_completion=payload["allowOvercompletion"],
1745            show_value_style=payload["showValueOnComplete"],
1746            display_only_objective=payload["isDisplayOnlyObjective"],
1747            complete_value_style=entity.ValueUIStyle(
1748                int(payload["completedValueStyle"])
1749            ),
1750            progress_value_style=entity.ValueUIStyle(
1751                int(payload["inProgressValueStyle"])
1752            ),
1753            ui_label=payload["uiLabel"],
1754            ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])),
1755        )
1756
1757    def _deserialize_activity_values(
1758        self, payload: typedefs.JSONObject, /
1759    ) -> activity.ActivityValues:
1760        team: typing.Optional[int] = None
1761        if raw_team := payload.get("team"):
1762            team = raw_team["basic"]["value"]
1763        return activity.ActivityValues(
1764            assists=payload["assists"]["basic"]["value"],
1765            deaths=payload["deaths"]["basic"]["value"],
1766            kills=payload["kills"]["basic"]["value"],
1767            is_completed=bool(payload["completed"]["basic"]["value"]),
1768            opponents_defeated=payload["opponentsDefeated"]["basic"]["value"],
1769            efficiency=payload["efficiency"]["basic"]["value"],
1770            kd_ratio=payload["killsDeathsRatio"]["basic"]["value"],
1771            kd_assists=payload["killsDeathsAssists"]["basic"]["value"],
1772            score=payload["score"]["basic"]["value"],
1773            duration=payload["activityDurationSeconds"]["basic"]["displayValue"],
1774            team=team,
1775            completion_reason=payload["completionReason"]["basic"]["displayValue"],
1776            fireteam_id=payload["fireteamId"]["basic"]["value"],
1777            start_seconds=payload["startSeconds"]["basic"]["value"],
1778            played_time=payload["timePlayedSeconds"]["basic"]["displayValue"],
1779            player_count=payload["playerCount"]["basic"]["value"],
1780            team_score=payload["teamScore"]["basic"]["value"],
1781        )
1782
1783    def deserialize_activity(
1784        self,
1785        payload: typedefs.JSONObject,
1786        /,
1787    ) -> activity.Activity:
1788        period = time.clean_date(payload["period"])
1789        details = payload["activityDetails"]
1790        ref_id = int(details["referenceId"])
1791        instance_id = int(details["instanceId"])
1792        mode = enums.GameMode(details["mode"])
1793        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1794        is_private = details["isPrivate"]
1795        membership_type = enums.MembershipType(int(details["membershipType"]))
1796
1797        # Since we're using the same fields for post activity method
1798        # this check is required since post activity doesn't values values
1799        values = self._deserialize_activity_values(payload["values"])
1800
1801        return activity.Activity(
1802            net=self._net,
1803            hash=ref_id,
1804            instance_id=instance_id,
1805            mode=mode,
1806            modes=modes,
1807            is_private=is_private,
1808            membership_type=membership_type,
1809            occurred_at=period,
1810            values=values,
1811        )
1812
1813    def deserialize_activities(
1814        self, payload: typedefs.JSONObject
1815    ) -> iterators.Iterator[activity.Activity]:
1816        return iterators.Iterator(
1817            [
1818                self.deserialize_activity(activity_)
1819                for activity_ in payload["activities"]
1820            ]
1821        )
1822
1823    def deserialize_extended_weapon_values(
1824        self, payload: typedefs.JSONObject
1825    ) -> activity.ExtendedWeaponValues:
1826
1827        assists: typing.Optional[int] = None
1828        if raw_assists := payload["values"].get("uniqueWeaponAssists"):
1829            assists = raw_assists["basic"]["value"]
1830        assists_damage: typing.Optional[int] = None
1831
1832        if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"):
1833            assists_damage = raw_assists_damage["basic"]["value"]
1834
1835        return activity.ExtendedWeaponValues(
1836            reference_id=int(payload["referenceId"]),
1837            kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"],
1838            precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][
1839                "value"
1840            ],
1841            assists=assists,
1842            assists_damage=assists_damage,
1843            precision_kills_percentage=(
1844                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"],
1845                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][
1846                    "displayValue"
1847                ],
1848            ),
1849        )
1850
1851    def _deserialize_extended_values(
1852        self, payload: typedefs.JSONObject
1853    ) -> activity.ExtendedValues:
1854        weapons: typing.Optional[
1855            collections.Collection[activity.ExtendedWeaponValues]
1856        ] = None
1857
1858        if raw_weapons := payload.get("weapons"):
1859            weapons = [
1860                self.deserialize_extended_weapon_values(value) for value in raw_weapons
1861            ]
1862
1863        return activity.ExtendedValues(
1864            precision_kills=payload["values"]["precisionKills"]["basic"]["value"],
1865            grenade_kills=payload["values"]["weaponKillsGrenade"]["basic"]["value"],
1866            melee_kills=payload["values"]["weaponKillsMelee"]["basic"]["value"],
1867            super_kills=payload["values"]["weaponKillsSuper"]["basic"]["value"],
1868            ability_kills=payload["values"]["weaponKillsAbility"]["basic"]["value"],
1869            weapons=weapons,
1870        )
1871
1872    def deserialize_post_activity_player(
1873        self, payload: typedefs.JSONObject, /
1874    ) -> activity.PostActivityPlayer:
1875        player = payload["player"]
1876
1877        class_hash: typedefs.NoneOr[int] = None
1878        if (class_hash := player.get("classHash")) is not None:
1879            class_hash = class_hash
1880
1881        race_hash: typedefs.NoneOr[int] = None
1882        if (race_hash := player.get("raceHash")) is not None:
1883            race_hash = race_hash
1884
1885        gender_hash: typedefs.NoneOr[int] = None
1886        if (gender_hash := player.get("genderHash")) is not None:
1887            gender_hash = gender_hash
1888
1889        character_class: undefined.UndefinedOr[str] = undefined.UNDEFINED
1890        if (
1891            character_class := player.get("characterClass")
1892        ) and not typedefs.is_unknown(character_class):
1893            character_class = character_class
1894
1895        character_level: typedefs.NoneOr[int] = None
1896        if (character_level := player.get("characterLevel")) is not None:
1897            character_level = character_level
1898
1899        return activity.PostActivityPlayer(
1900            standing=int(payload["standing"]),
1901            score=int(payload["score"]["basic"]["value"]),
1902            character_id=payload["characterId"],
1903            destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]),
1904            character_class=character_class,
1905            character_level=character_level,
1906            race_hash=race_hash,
1907            gender_hash=gender_hash,
1908            class_hash=class_hash,
1909            light_level=int(player["lightLevel"]),
1910            emblem_hash=int(player["emblemHash"]),
1911            values=self._deserialize_activity_values(payload["values"]),
1912            extended_values=self._deserialize_extended_values(payload["extended"]),
1913        )
1914
1915    def _deserialize_post_activity_team(
1916        self, payload: typedefs.JSONObject
1917    ) -> activity.PostActivityTeam:
1918        return activity.PostActivityTeam(
1919            id=payload["teamId"],
1920            is_defeated=bool(payload["standing"]["basic"]["value"]),
1921            score=int(payload["score"]["basic"]["value"]),
1922            name=payload["teamName"],
1923        )
1924
1925    def deserialize_post_activity(
1926        self, payload: typedefs.JSONObject
1927    ) -> activity.PostActivity:
1928        period = time.clean_date(payload["period"])
1929        details = payload["activityDetails"]
1930        ref_id = int(details["referenceId"])
1931        instance_id = int(details["instanceId"])
1932        mode = enums.GameMode(details["mode"])
1933        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1934        is_private = details["isPrivate"]
1935        membership_type = enums.MembershipType(int(details["membershipType"]))
1936        return activity.PostActivity(
1937            net=self._net,
1938            hash=ref_id,
1939            membership_type=membership_type,
1940            instance_id=instance_id,
1941            mode=mode,
1942            modes=modes,
1943            is_private=is_private,
1944            occurred_at=period,
1945            starting_phase=int(payload["startingPhaseIndex"]),
1946            players=[
1947                self.deserialize_post_activity_player(player)
1948                for player in payload["entries"]
1949            ],
1950            teams=[
1951                self._deserialize_post_activity_team(team) for team in payload["teams"]
1952            ],
1953        )
1954
1955    def _deserialize_aggregated_activity_values(
1956        self, payload: typedefs.JSONObject
1957    ) -> activity.AggregatedActivityValues:
1958        # This ID is always the same for all aggregated values.
1959        activity_id = int(payload["fastestCompletionMsForActivity"]["activityId"])
1960
1961        return activity.AggregatedActivityValues(
1962            id=activity_id,
1963            fastest_completion_time=(
1964                int(payload["fastestCompletionMsForActivity"]["basic"]["value"]),
1965                payload["fastestCompletionMsForActivity"]["basic"]["displayValue"],
1966            ),
1967            completions=int(payload["activityCompletions"]["basic"]["value"]),
1968            kills=int(payload["activityKills"]["basic"]["value"]),
1969            deaths=int(payload["activityDeaths"]["basic"]["value"]),
1970            assists=int(payload["activityAssists"]["basic"]["value"]),
1971            seconds_played=(
1972                int(payload["activitySecondsPlayed"]["basic"]["value"]),
1973                payload["activitySecondsPlayed"]["basic"]["displayValue"],
1974            ),
1975            wins=int(payload["activityWins"]["basic"]["value"]),
1976            goals_missed=int(payload["activityGoalsMissed"]["basic"]["value"]),
1977            special_actions=int(payload["activitySpecialActions"]["basic"]["value"]),
1978            best_goals_hit=int(payload["activityBestGoalsHit"]["basic"]["value"]),
1979            best_single_score=int(
1980                payload["activityBestSingleGameScore"]["basic"]["value"]
1981            ),
1982            goals_hit=int(payload["activityGoalsHit"]["basic"]["value"]),
1983            special_score=int(payload["activitySpecialScore"]["basic"]["value"]),
1984            kd_assists=int(payload["activityKillsDeathsAssists"]["basic"]["value"]),
1985            kd_ratio=float(
1986                payload["activityKillsDeathsAssists"]["basic"]["displayValue"]
1987            ),
1988            precision_kills=int(payload["activityPrecisionKills"]["basic"]["value"]),
1989        )
1990
1991    def deserialize_aggregated_activity(
1992        self, payload: typedefs.JSONObject
1993    ) -> activity.AggregatedActivity:
1994        return activity.AggregatedActivity(
1995            hash=int(payload["activityHash"]),
1996            values=self._deserialize_aggregated_activity_values(payload["values"]),
1997        )
1998
1999    def deserialize_aggregated_activities(
2000        self, payload: typedefs.JSONObject
2001    ) -> iterators.Iterator[activity.AggregatedActivity]:
2002        return iterators.Iterator(
2003            [
2004                self.deserialize_aggregated_activity(activity)
2005                for activity in payload["activities"]
2006            ]
2007        )
2008
2009    def deserialize_linked_profiles(
2010        self, payload: typedefs.JSONObject
2011    ) -> profile.LinkedProfile:
2012        bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"])
2013        error_profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2014        profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2015
2016        if raw_profile := payload.get("profiles"):
2017            for pfile in raw_profile:
2018                profiles_vec.append(self.deserialize_destiny_membership(pfile))
2019
2020        if raw_profiles_with_errors := payload.get("profilesWithErrors"):
2021            for raw_error_pfile in raw_profiles_with_errors:
2022                if error_pfile := raw_error_pfile.get("infoCard"):
2023                    error_profiles_vec.append(
2024                        self.deserialize_destiny_membership(error_pfile)
2025                    )
2026
2027        return profile.LinkedProfile(
2028            net=self._net,
2029            bungie=bungie_user,
2030            profiles=profiles_vec,
2031            profiles_with_errors=error_profiles_vec,
2032        )
2033
2034    def deserialize_clan_banners(
2035        self, payload: typedefs.JSONObject
2036    ) -> collections.Sequence[clans.ClanBanner]:
2037        banners_seq: typing.MutableSequence[clans.ClanBanner] = []
2038        if banners := payload.get("clanBannerDecals"):
2039            for k, v in banners.items():
2040                banner_obj = clans.ClanBanner(
2041                    id=int(k),
2042                    foreground=assets.Image(v["foregroundPath"]),
2043                    background=assets.Image(v["backgroundPath"]),
2044                )
2045                banners_seq.append(banner_obj)
2046        return banners_seq
2047
2048    def deserialize_public_milestone_content(
2049        self, payload: typedefs.JSONObject
2050    ) -> milestones.MilestoneContent:
2051        items_categoris: typedefs.NoneOr[milestones.MilestoneItems] = None
2052        if raw_categories := payload.get("itemCategories"):
2053            for item in raw_categories:
2054                title = undefined.UNDEFINED
2055                if raw_title := item.get("title"):
2056                    if raw_title != typedefs.Unknown:
2057                        title = raw_title
2058                if raw_hashes := item.get("itemHashes"):
2059                    hashes: collections.Sequence[int] = raw_hashes
2060
2061                items_categoris = milestones.MilestoneItems(title=title, hashes=hashes)
2062
2063        about = undefined.UNDEFINED
2064        if (raw_about := payload["about"]) != typedefs.Unknown:
2065            about = raw_about
2066
2067        status = undefined.UNDEFINED
2068        if (raw_status := payload["status"]) != typedefs.Unknown:
2069            status = raw_status
2070
2071        tips: typing.MutableSequence[undefined.UndefinedOr[str]] = []
2072        if raw_tips := payload.get("tips"):
2073            for raw_tip in raw_tips:
2074                if raw_tip == typedefs.Unknown:
2075                    raw_tip = undefined.UNDEFINED
2076                tips.append(raw_tip)
2077
2078        return milestones.MilestoneContent(
2079            about=about, status=status, tips=tips, items=items_categoris
2080        )
2081
2082    def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend:
2083        name = undefined.UNDEFINED
2084        if (raw_name := payload["bungieGlobalDisplayName"]) != typedefs.Unknown:
2085            name = raw_name
2086
2087        bungie_user: typedefs.NoneOr[user.BungieUser] = None
2088
2089        if raw_bungie_user := payload.get("bungieNetUser"):
2090            bungie_user = self.deserialize_bungie_user(raw_bungie_user)
2091
2092        return friends.Friend(
2093            net=self._net,
2094            id=int(payload["lastSeenAsMembershipId"]),
2095            name=name,
2096            code=payload.get("bungieGlobalDisplayNameCode"),
2097            relationship=enums.Relationship(payload["relationship"]),
2098            user=bungie_user,
2099            online_status=enums.Presence(payload["onlineStatus"]),
2100            online_title=payload["onlineTitle"],
2101            type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]),
2102        )
2103
2104    def deserialize_friends(
2105        self, payload: typedefs.JSONObject
2106    ) -> collections.Sequence[friends.Friend]:
2107        mut_seq: typing.MutableSequence[friends.Friend] = []
2108        if raw_friends := payload.get("friends"):
2109            for friend in raw_friends:
2110                mut_seq.append(self.deserialize_friend(friend))
2111        return mut_seq
2112
2113    def deserialize_friend_requests(
2114        self, payload: typedefs.JSONObject
2115    ) -> friends.FriendRequestView:
2116        incoming: typing.MutableSequence[friends.Friend] = []
2117        outgoing: typing.MutableSequence[friends.Friend] = []
2118
2119        if raw_incoming_requests := payload.get("incomingRequests"):
2120            for incoming_request in raw_incoming_requests:
2121                incoming.append(self.deserialize_friend(incoming_request))
2122
2123        if raw_outgoing_requests := payload.get("outgoingRequests"):
2124            for outgoing_request in raw_outgoing_requests:
2125                outgoing.append(self.deserialize_friend(outgoing_request))
2126
2127        return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)
2128
2129    def _set_fireteam_fields(
2130        self, payload: typedefs.JSONObject, total_results: typing.Optional[int] = None
2131    ) -> fireteams.Fireteam:
2132        activity_type = fireteams.FireteamActivity(payload["activityType"])
2133        return fireteams.Fireteam(
2134            id=int(payload["fireteamId"]),
2135            group_id=int(payload["groupId"]),
2136            platform=fireteams.FireteamPlatform(payload["platform"]),
2137            is_immediate=payload["isImmediate"],
2138            activity_type=activity_type,
2139            owner_id=int(payload["ownerMembershipId"]),
2140            player_slot_count=payload["playerSlotCount"],
2141            available_player_slots=payload["availablePlayerSlotCount"],
2142            available_alternate_slots=payload["availableAlternateSlotCount"],
2143            title=payload["title"],
2144            date_created=time.clean_date(payload["dateCreated"]),
2145            is_public=payload["isPublic"],
2146            locale=fireteams.FireteamLanguage(payload["locale"]),
2147            is_valid=payload["isValid"],
2148            last_modified=time.clean_date(payload["datePlayerModified"]),
2149            total_results=total_results or 0,
2150        )
2151
2152    def deserialize_fireteams(
2153        self, payload: typedefs.JSONObject
2154    ) -> typedefs.NoneOr[collections.Sequence[fireteams.Fireteam]]:
2155        fireteams_: typing.MutableSequence[fireteams.Fireteam] = []
2156
2157        result: list[typedefs.JSONObject]
2158        if not (result := payload["results"]):
2159            return None
2160        for elem in result:
2161            fireteams_.append(
2162                self._set_fireteam_fields(
2163                    elem, total_results=int(payload["totalResults"])
2164                )
2165            )
2166        return fireteams_
2167
2168    def deserialize_fireteam_destiny_users(
2169        self, payload: typedefs.JSONObject
2170    ) -> fireteams.FireteamUser:
2171        destiny_obj = self.deserialize_destiny_membership(payload)
2172        # We could helpers.just return a DestinyMembership object but this is
2173        # missing the fireteam display name and id fields.
2174        return fireteams.FireteamUser(
2175            net=self._net,
2176            id=destiny_obj.id,
2177            code=destiny_obj.code,
2178            icon=destiny_obj.icon,
2179            types=destiny_obj.types,
2180            type=destiny_obj.type,
2181            is_public=destiny_obj.is_public,
2182            crossave_override=destiny_obj.crossave_override,
2183            name=destiny_obj.name,
2184            last_seen_name=destiny_obj.last_seen_name,
2185            fireteam_display_name=payload["FireteamDisplayName"],
2186            fireteam_membership_id=enums.MembershipType(
2187                payload["FireteamMembershipType"]
2188            ),
2189        )
2190
2191    def deserialize_fireteam_members(
2192        self, payload: typedefs.JSONObject, *, alternatives: bool = False
2193    ) -> typing.Optional[collections.Sequence[fireteams.FireteamMember]]:
2194        members_: list[fireteams.FireteamMember] = []
2195        if members := payload.get("Members" if not alternatives else "Alternates"):
2196            for member in members:
2197                bungie_fields = self.deserialize_partial_bungie_user(member)
2198                members_fields = fireteams.FireteamMember(
2199                    destiny_user=self.deserialize_fireteam_destiny_users(member),
2200                    has_microphone=member["hasMicrophone"],
2201                    character_id=int(member["characterId"]),
2202                    date_joined=time.clean_date(member["dateJoined"]),
2203                    last_platform_invite_date=time.clean_date(
2204                        member["lastPlatformInviteAttemptDate"]
2205                    ),
2206                    last_platform_invite_result=int(
2207                        member["lastPlatformInviteAttemptResult"]
2208                    ),
2209                    net=self._net,
2210                    name=bungie_fields.name,
2211                    id=bungie_fields.id,
2212                    icon=bungie_fields.icon,
2213                    is_public=bungie_fields.is_public,
2214                    crossave_override=bungie_fields.crossave_override,
2215                    types=bungie_fields.types,
2216                    type=bungie_fields.type,
2217                )
2218                members_.append(members_fields)
2219        else:
2220            return None
2221        return members_
2222
2223    def deserialize_available_fireteams(
2224        self,
2225        data: typedefs.JSONObject,
2226        *,
2227        no_results: bool = False,
2228    ) -> typing.Union[
2229        fireteams.AvailableFireteam, collections.Sequence[fireteams.AvailableFireteam]
2230    ]:
2231        fireteams_: list[fireteams.AvailableFireteam] = []
2232
2233        # This needs to be used outside the results
2234        # JSON key.
2235        if no_results is True:
2236            payload = data
2237
2238        if result := payload.get("results"):
2239
2240            for fireteam in result:
2241                found_fireteams = self._set_fireteam_fields(fireteam["Summary"])
2242                fireteams_fields = fireteams.AvailableFireteam(
2243                    id=found_fireteams.id,
2244                    group_id=found_fireteams.group_id,
2245                    platform=found_fireteams.platform,
2246                    activity_type=found_fireteams.activity_type,
2247                    is_immediate=found_fireteams.is_immediate,
2248                    is_public=found_fireteams.is_public,
2249                    is_valid=found_fireteams.is_valid,
2250                    owner_id=found_fireteams.owner_id,
2251                    player_slot_count=found_fireteams.player_slot_count,
2252                    available_player_slots=found_fireteams.available_player_slots,
2253                    available_alternate_slots=found_fireteams.available_alternate_slots,
2254                    title=found_fireteams.title,
2255                    date_created=found_fireteams.date_created,
2256                    locale=found_fireteams.locale,
2257                    last_modified=found_fireteams.last_modified,
2258                    total_results=found_fireteams.total_results,
2259                    members=self.deserialize_fireteam_members(payload),
2260                    alternatives=self.deserialize_fireteam_members(
2261                        payload, alternatives=True
2262                    ),
2263                )
2264            fireteams_.append(fireteams_fields)
2265            if no_results:
2266                return fireteams_fields
2267        return fireteams_
2268
2269    def deserialize_fireteam_party(
2270        self, payload: typedefs.JSONObject
2271    ) -> fireteams.FireteamParty:
2272        last_destination_hash: typing.Optional[int] = None
2273        if raw_dest_hash := payload.get("lastOrbitedDestinationHash"):
2274            last_destination_hash = int(raw_dest_hash)
2275
2276        return fireteams.FireteamParty(
2277            members=[
2278                self._deserialize_fireteam_party_member(member)
2279                for member in payload["partyMembers"]
2280            ],
2281            activity=self._deserialize_fireteam_party_current_activity(
2282                payload["currentActivity"]
2283            ),
2284            settings=self._deserialize_fireteam_party_settings(payload["joinability"]),
2285            last_destination_hash=last_destination_hash,
2286            tracking=payload["tracking"],
2287        )
2288
2289    def _deserialize_fireteam_party_member(
2290        self, payload: typedefs.JSONObject
2291    ) -> fireteams.FireteamPartyMember:
2292
2293        status = fireteams.FireteamPartyMemberState(payload["status"])
2294        displayname: undefined.UndefinedOr[str] = undefined.UNDEFINED
2295        if raw_name := payload.get("displayName"):
2296            displayname = raw_name
2297
2298        return fireteams.FireteamPartyMember(
2299            membership_id=int(payload["membershipId"]),
2300            emblem_hash=int(payload["emblemHash"]),
2301            status=status,
2302            display_name=displayname,
2303        )
2304
2305    def _deserialize_fireteam_party_current_activity(
2306        self, payload: typedefs.JSONObject
2307    ) -> fireteams.FireteamPartyCurrentActivity:
2308        start_date: typing.Optional[datetime.datetime] = None
2309        if raw_start_date := payload.get("startTime"):
2310            start_date = time.clean_date(raw_start_date)
2311
2312        end_date: typing.Optional[datetime.datetime] = None
2313        if raw_end_date := payload.get("endTime"):
2314            end_date = time.clean_date(raw_end_date)
2315        return fireteams.FireteamPartyCurrentActivity(
2316            start_time=start_date,
2317            end_time=end_date,
2318            score=float(payload["score"]),
2319            highest_opposing_score=float(payload["highestOpposingFactionScore"]),
2320            opponenst_count=int(payload["numberOfOpponents"]),
2321            player_count=int(payload["numberOfPlayers"]),
2322        )
2323
2324    def _deserialize_fireteam_party_settings(
2325        self, payload: typedefs.JSONObject
2326    ) -> fireteams.FireteamPartySettings:
2327        closed_reasons = enums.ClosedReasons(payload["closedReasons"])
2328        return fireteams.FireteamPartySettings(
2329            open_slots=int(payload["openSlots"]),
2330            privacy_setting=enums.PrivacySetting(int(payload["privacySetting"])),
2331            closed_reasons=closed_reasons,
2332        )
2333
2334    def deserialize_seasonal_artifact(
2335        self, payload: typedefs.JSONObject
2336    ) -> season.Artifact:
2337        if raw_artifact := payload.get("seasonalArtifact"):
2338            if points := raw_artifact.get("pointProgression"):
2339                points_prog = progressions.Progression(
2340                    hash=points["progressionHash"],
2341                    level=points["level"],
2342                    cap=points["levelCap"],
2343                    daily_limit=points["dailyLimit"],
2344                    weekly_limit=points["weeklyLimit"],
2345                    current_progress=points["currentProgress"],
2346                    daily_progress=points["dailyProgress"],
2347                    needed=points["progressToNextLevel"],
2348                    next_level=points["nextLevelAt"],
2349                )
2350
2351            if bonus := raw_artifact.get("powerBonusProgression"):
2352                power_bonus_prog = progressions.Progression(
2353                    hash=bonus["progressionHash"],
2354                    level=bonus["level"],
2355                    cap=bonus["levelCap"],
2356                    daily_limit=bonus["dailyLimit"],
2357                    weekly_limit=bonus["weeklyLimit"],
2358                    current_progress=bonus["currentProgress"],
2359                    daily_progress=bonus["dailyProgress"],
2360                    needed=bonus["progressToNextLevel"],
2361                    next_level=bonus["nextLevelAt"],
2362                )
2363            artifact = season.Artifact(
2364                net=self._net,
2365                hash=raw_artifact["artifactHash"],
2366                power_bonus=raw_artifact["powerBonus"],
2367                acquired_points=raw_artifact["pointsAcquired"],
2368                bonus=power_bonus_prog,
2369                points=points_prog,
2370            )
2371        return artifact
2372
2373    def deserialize_profile_progression(
2374        self, payload: typedefs.JSONObject
2375    ) -> profile.ProfileProgression:
2376        return profile.ProfileProgression(
2377            artifact=self.deserialize_seasonal_artifact(payload["data"]),
2378            checklist={
2379                int(check_id): checklists
2380                for check_id, checklists in payload["data"]["checklists"].items()
2381            },
2382        )
2383
2384    def deserialize_instanced_item(
2385        self, payload: typedefs.JSONObject
2386    ) -> items.ItemInstance:
2387        damage_type_hash: typing.Optional[int] = None
2388        if raw_damagetype_hash := payload.get("damageTypeHash"):
2389            damage_type_hash = int(raw_damagetype_hash)
2390
2391        required_hashes: typing.Optional[collections.Collection[int]] = None
2392        if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"):
2393            required_hashes = [int(raw_hash) for raw_hash in raw_required_hashes]
2394
2395        breaker_type: typing.Optional[items.ItemBreakerType] = None
2396        if raw_break_type := payload.get("breakerType"):
2397            breaker_type = items.ItemBreakerType(int(raw_break_type))
2398
2399        breaker_type_hash: typing.Optional[int] = None
2400        if raw_break_type_hash := payload.get("breakerTypeHash"):
2401            breaker_type_hash = int(raw_break_type_hash)
2402
2403        energy: typing.Optional[items.ItemEnergy] = None
2404        if raw_energy := payload.get("energy"):
2405            energy = self.deserialize_item_energy(raw_energy)
2406
2407        primary_stats = None
2408        if raw_primary_stats := payload.get("primaryStat"):
2409            primary_stats = self.deserialize_item_stats_view(raw_primary_stats)
2410
2411        return items.ItemInstance(
2412            damage_type=enums.DamageType(int(payload["damageType"])),
2413            damage_type_hash=damage_type_hash,
2414            primary_stat=primary_stats,
2415            item_level=int(payload["itemLevel"]),
2416            quality=int(payload["quality"]),
2417            is_equipped=payload["isEquipped"],
2418            can_equip=payload["canEquip"],
2419            equip_required_level=int(payload["equipRequiredLevel"]),
2420            required_equip_unlock_hashes=required_hashes,
2421            cant_equip_reason=int(payload["cannotEquipReason"]),
2422            breaker_type=breaker_type,
2423            breaker_type_hash=breaker_type_hash,
2424            energy=energy,
2425        )
2426
2427    def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy:
2428        energy_hash: typing.Optional[int] = None
2429        if raw_energy_hash := payload.get("energyTypeHash"):
2430            energy_hash = int(raw_energy_hash)
2431
2432        return items.ItemEnergy(
2433            hash=energy_hash,
2434            type=items.ItemEnergyType(int(payload["energyType"])),
2435            capacity=int(payload["energyCapacity"]),
2436            used_energy=int(payload["energyUsed"]),
2437            unused_energy=int(payload["energyUnused"]),
2438        )
2439
2440    def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk:
2441        perk_hash: typing.Optional[int] = None
2442        if raw_perk_hash := payload.get("perkHash"):
2443            perk_hash = int(raw_perk_hash)
2444
2445        return items.ItemPerk(
2446            hash=perk_hash,
2447            icon=assets.Image(payload["iconPath"]),
2448            is_active=payload["isActive"],
2449            is_visible=payload["visible"],
2450        )
2451
2452    def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket:
2453        plug_hash: typing.Optional[int] = None
2454        if raw_plug_hash := payload.get("plugHash"):
2455            plug_hash = int(raw_plug_hash)
2456
2457        enable_fail_indexes: typing.Optional[list[int]] = None
2458        if raw_indexes := payload.get("enableFailIndexes"):
2459            enable_fail_indexes = [int(index) for index in raw_indexes]
2460
2461        return items.ItemSocket(
2462            plug_hash=plug_hash,
2463            is_enabled=payload["isEnabled"],
2464            enable_fail_indexes=enable_fail_indexes,
2465            is_visible=payload.get("visible"),
2466        )
2467
2468    def deserialize_item_stats_view(
2469        self, payload: typedefs.JSONObject
2470    ) -> items.ItemStatsView:
2471        return items.ItemStatsView(
2472            stat_hash=payload.get("statHash"), value=payload.get("value")
2473        )
2474
2475    def deserialize_plug_item_state(
2476        self, payload: typedefs.JSONObject
2477    ) -> items.PlugItemState:
2478        item_hash: typing.Optional[int] = None
2479        if raw_item_hash := payload.get("plugItemHash"):
2480            item_hash = int(raw_item_hash)
2481
2482        insert_fail_indexes: typedefs.NoneOr[list[int]] = None
2483        if raw_fail_indexes := payload.get("insertFailIndexes"):
2484            insert_fail_indexes = [int(k) for k in raw_fail_indexes]
2485
2486        enable_fail_indexes: typedefs.NoneOr[list[int]] = None
2487        if raw_enabled_indexes := payload.get("enableFailIndexes"):
2488            enable_fail_indexes = [int(k) for k in raw_enabled_indexes]
2489
2490        return items.PlugItemState(
2491            item_hash=item_hash,
2492            insert_fail_indexes=insert_fail_indexes,
2493            enable_fail_indexes=enable_fail_indexes,
2494            is_enabled=payload["enabled"],
2495            can_insert=payload["canInsert"],
2496        )

The base deserialization factory class for all aiobungie objects.

Highly inspired hikari entity factory used to deserialize JSON responses from the REST client and turning them into a aiobungie.crates Python classes.

Factory(net: aiobungie.traits.Netrunner)
70    def __init__(self, net: traits.Netrunner) -> None:
71        self._net = net
def deserialize_bungie_user(self, data: dict[str, typing.Any]) -> aiobungie.crates.user.BungieUser:
73    def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser:
74        return user.BungieUser(
75            id=int(data["membershipId"]),
76            created_at=time.clean_date(data["firstAccess"]),
77            name=data.get("cachedBungieGlobalDisplayName", undefined.UNDEFINED),
78            is_deleted=data["isDeleted"],
79            about=data["about"],
80            updated_at=time.clean_date(data["lastUpdate"]),
81            psn_name=data.get("psnDisplayName", None),
82            stadia_name=data.get("stadiaDisplayName", None),
83            steam_name=data.get("steamDisplayName", None),
84            twitch_name=data.get("twitchDisplayName", None),
85            blizzard_name=data.get("blizzardDisplayName", None),
86            status=data["statusText"],
87            locale=data["locale"],
88            picture=assets.Image(path=str(data["profilePicturePath"])),
89            code=data.get("cachedBungieGlobalDisplayNameCode", None),
90            unique_name=data.get("uniqueName", None),
91            theme_id=int(data["profileTheme"]),
92            show_activity=bool(data["showActivity"]),
93            theme_name=data["profileThemeName"],
94            display_title=data["userTitleDisplay"],
95        )

Deserialize a raw JSON Bungie.net user only payload into a user object.

This only returns the Bungie.net user and not the Destiny memberships.

Parameters
Returns
def deserialize_partial_bungie_user( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.PartialBungieUser:
 97    def deserialize_partial_bungie_user(
 98        self, payload: typedefs.JSONObject
 99    ) -> user.PartialBungieUser:
100        return user.PartialBungieUser(
101            net=self._net,
102            types=[
103                enums.MembershipType(type_)
104                for type_ in payload.get("applicableMembershipTypes", [])
105            ],
106            name=payload.get("displayName", undefined.UNDEFINED),
107            id=int(payload["membershipId"]),
108            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
109            is_public=payload["isPublic"],
110            icon=assets.Image(payload.get("iconPath", "")),
111            type=enums.MembershipType(payload["membershipType"]),
112        )

Deserialize a raw JSON of a partial bungieNetUserInfo.

A partial user is a bungie.net user payload with missing information from the main BungieUser object.

Parameters
Returns
def deserialize_destiny_membership( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.DestinyMembership:
114    def deserialize_destiny_membership(
115        self, payload: typedefs.JSONObject
116    ) -> user.DestinyMembership:
117        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
118        if (
119            raw_name := payload.get("bungieGlobalDisplayName", "")
120        ) and not typedefs.is_unknown(raw_name):
121            name = raw_name
122
123        return user.DestinyMembership(
124            net=self._net,
125            id=int(payload["membershipId"]),
126            name=name,
127            code=payload.get("bungieGlobalDisplayNameCode", None),
128            last_seen_name=payload.get("LastSeenDisplayName")
129            or payload.get("displayName")  # noqa: W503
130            or "",  # noqa: W503
131            type=enums.MembershipType(payload["membershipType"]),
132            is_public=payload["isPublic"],
133            crossave_override=enums.MembershipType(payload["crossSaveOverride"]),
134            icon=assets.Image(payload.get("iconPath", "")),
135            types=[
136                enums.MembershipType(type_)
137                for type_ in payload.get("applicableMembershipTypes", [])
138            ],
139        )

Deserialize a raw JSON of destinyUserInfo destiny membership information.

Parameters
Returns
  • aiobungie.crates.user.DestinyMembership: A Destiny 2 membership.
def deserialize_destiny_memberships( self, data: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.DestinyMembership]:
141    def deserialize_destiny_memberships(
142        self, data: typedefs.JSONArray
143    ) -> collections.Sequence[user.DestinyMembership]:
144        return [self.deserialize_destiny_membership(membership) for membership in data]

Deserialize a raw JSON payload/array of destinyUserInfo.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.user.DestinyMembership]: A sequence of Destiny 2 memberships.
def deserialize_user(self, data: dict[str, typing.Any]) -> aiobungie.crates.user.User:
146    def deserialize_user(self, data: typedefs.JSONObject) -> user.User:
147
148        primary_membership_id: typing.Optional[int] = None
149        if raw_primary_id := data.get("primaryMembershipId"):
150            primary_membership_id = int(raw_primary_id)
151
152        return user.User(
153            bungie=self.deserialize_bungie_user(data["bungieNetUser"]),
154            destiny=self.deserialize_destiny_memberships(data["destinyMemberships"]),
155            primary_membership_id=primary_membership_id,
156        )

Deserialize a raw JSON results of fetched user memberships and Bungie.net user its their id.

Parameters
Returns
def deserialize_searched_user( self, payload: dict[str, typing.Any]) -> aiobungie.crates.user.SearchableDestinyUser:
158    def deserialize_searched_user(
159        self, payload: typedefs.JSONObject
160    ) -> user.SearchableDestinyUser:
161        name: undefined.UndefinedOr[str] = undefined.UNDEFINED
162        if (raw_name := payload["bungieGlobalDisplayName"]) and not typedefs.is_unknown(
163            raw_name
164        ):
165            name = raw_name
166
167        code: typing.Optional[int] = None
168        if raw_code := payload.get("bungieGlobalDisplayNameCode"):
169            code = int(raw_code)
170
171        bungie_id: typing.Optional[int] = None
172        if raw_bungie_id := payload.get("bungieNetMembershipId"):
173            bungie_id = int(raw_bungie_id)
174
175        return user.SearchableDestinyUser(
176            name=name,
177            code=code,
178            bungie_id=bungie_id,
179            memberships=self.deserialize_destiny_memberships(
180                payload["destinyMemberships"]
181            ),
182        )

Deserialize the results of user search details.

Parameters
Returns
def deserialize_user_credentials( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.UserCredentials]:
184    def deserialize_user_credentials(
185        self, payload: typedefs.JSONArray
186    ) -> collections.Sequence[user.UserCredentials]:
187        return [
188            user.UserCredentials(
189                type=enums.CredentialType(int(creds["credentialType"])),
190                display_name=creds["credentialDisplayName"],
191                is_public=creds["isPublic"],
192                self_as_string=creds.get("credentialAsString", undefined.UNDEFINED),
193            )
194            for creds in payload
195        ]

Deserialize a JSON array of Bungie user credentials.

Parameters
Returns
def deserialize_user_themes( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.user.UserThemes]:
197    def deserialize_user_themes(
198        self, payload: typedefs.JSONArray
199    ) -> collections.Sequence[user.UserThemes]:
200        return [
201            user.UserThemes(
202                id=int(entry["userThemeId"]),
203                name=entry["userThemeName"]
204                if "userThemeName" in entry
205                else undefined.UNDEFINED,
206                description=entry["userThemeDescription"]
207                if "userThemeDescription" in entry
208                else undefined.UNDEFINED,
209            )
210            for entry in payload
211        ]

Deserialize a raw JSON array of Bungie user themes.

Parameters
Returns
  • collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of bungie user themes.
def deserialize_clan(self, payload: dict[str, typing.Any]) -> aiobungie.crates.clans.Clan:
213    def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan:
214
215        # This is kinda redundant
216        data = payload
217
218        # This is always outside the details.
219        current_user_map: typing.Optional[
220            collections.Mapping[str, clans.ClanMember]
221        ] = None
222        if raw_current_user_map := payload.get("currentUserMemberMap"):
223            current_user_map = {
224                membership_type: self.deserialize_clan_member(membership)
225                for membership_type, membership in raw_current_user_map.items()
226            }
227
228        try:
229            data = payload["detail"]
230        except KeyError:
231            pass
232
233        id = data["groupId"]
234        name = data["name"]
235        created_at = data["creationDate"]
236        member_count = data["memberCount"]
237        about = data["about"]
238        motto = data["motto"]
239        is_public = data["isPublic"]
240        banner = assets.Image(str(data["bannerPath"]))
241        avatar = assets.Image(str(data["avatarPath"]))
242        tags = data["tags"]
243        type = data["groupType"]
244
245        features = data["features"]
246        features_obj = clans.ClanFeatures(
247            max_members=features["maximumMembers"],
248            max_membership_types=features["maximumMembershipsOfGroupType"],
249            capabilities=features["capabilities"],
250            membership_types=features["membershipTypes"],
251            invite_permissions=features["invitePermissionOverride"],
252            update_banner_permissions=features["updateBannerPermissionOverride"],
253            update_culture_permissions=features["updateCulturePermissionOverride"],
254            join_level=features["joinLevel"],
255        )
256
257        information: typedefs.JSONObject = data["clanInfo"]
258        progression: collections.Mapping[int, progressions.Progression] = {
259            int(prog_hash): self.deserialize_progressions(prog)
260            for prog_hash, prog in information["d2ClanProgressions"].items()
261        }
262
263        founder: typedefs.NoneOr[clans.ClanMember] = None
264        if raw_founder := payload.get("founder"):
265            founder = self.deserialize_clan_member(raw_founder)
266
267        return clans.Clan(
268            net=self._net,
269            id=int(id),
270            name=name,
271            type=enums.GroupType(type),
272            created_at=time.clean_date(created_at),
273            member_count=member_count,
274            motto=motto,
275            about=about,
276            is_public=is_public,
277            banner=banner,
278            avatar=avatar,
279            tags=tags,
280            features=features_obj,
281            owner=founder,
282            progressions=progression,
283            call_sign=information["clanCallsign"],
284            banner_data=information["clanBannerData"],
285            chat_security=data["chatSecurity"],
286            conversation_id=int(data["conversationId"]),
287            allow_chat=data["allowChat"],
288            theme=data["theme"],
289            current_user_membership=current_user_map,
290        )

Deserialize a raw JSON payload of Bungie clan information.

Parameters
Returns
def deserialize_clan_member( self, data: dict[str, typing.Any], /) -> aiobungie.crates.clans.ClanMember:
292    def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember:
293        destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"])
294        return clans.ClanMember(
295            net=self._net,
296            last_seen_name=destiny_user.last_seen_name,
297            id=destiny_user.id,
298            name=destiny_user.name,
299            icon=destiny_user.icon,
300            last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])),
301            group_id=int(data["groupId"]),
302            joined_at=time.clean_date(data["joinDate"]),
303            types=destiny_user.types,
304            is_public=destiny_user.is_public,
305            type=destiny_user.type,
306            code=destiny_user.code,
307            is_online=data["isOnline"],
308            crossave_override=destiny_user.crossave_override,
309            bungie=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"])
310            if "bungieNetUserInfo" in data
311            else None,
312            member_type=enums.ClanMemberType(int(data["memberType"])),
313        )

Deserialize a JSON payload of a clan member information.

Parameters
Returns
def deserialize_clan_members( self, data: dict[str, typing.Any], /) -> aiobungie.Iterator[aiobungie.crates.clans.ClanMember]:
315    def deserialize_clan_members(
316        self, data: typedefs.JSONObject, /
317    ) -> iterators.Iterator[clans.ClanMember]:
318        return iterators.Iterator(
319            [self.deserialize_clan_member(member) for member in data["results"]]
320        )

Deserialize a JSON payload of a clan members information.

Parameters
Returns
def deserialize_group_member( self, payload: dict[str, typing.Any]) -> aiobungie.crates.clans.GroupMember:
322    def deserialize_group_member(
323        self, payload: typedefs.JSONObject
324    ) -> clans.GroupMember:
325        member = payload["member"]
326        return clans.GroupMember(
327            net=self._net,
328            join_date=time.clean_date(member["joinDate"]),
329            group_id=int(member["groupId"]),
330            member_type=enums.ClanMemberType(member["memberType"]),
331            is_online=member["isOnline"],
332            last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])),
333            inactive_memberships=payload.get("areAllMembershipsInactive", None),
334            member=self.deserialize_destiny_membership(member["destinyUserInfo"]),
335            group=self.deserialize_clan(payload["group"]),
336        )

Deserialize a JSON payload of group information for a member.

Parameters
Returns
def deserialize_clan_conversations( self, payload: list[typing.Any]) -> collections.abc.Sequence[aiobungie.crates.clans.ClanConversation]:
354    def deserialize_clan_conversations(
355        self, payload: typedefs.JSONArray
356    ) -> collections.Sequence[clans.ClanConversation]:
357        return [self._deserialize_clan_conversation(conv) for conv in payload]

Deserialize a JSON array of a clan conversations information.

Parameters
Returns
def deserialize_app_owner( self, payload: dict[str, typing.Any]) -> aiobungie.crates.application.ApplicationOwner:
359    def deserialize_app_owner(
360        self, payload: typedefs.JSONObject
361    ) -> application.ApplicationOwner:
362        return application.ApplicationOwner(
363            net=self._net,
364            name=payload.get("bungieGlobalDisplayName", undefined.UNDEFINED),
365            id=int(payload["membershipId"]),
366            type=enums.MembershipType(payload["membershipType"]),
367            icon=assets.Image(str(payload["iconPath"])),
368            is_public=payload["isPublic"],
369            code=payload.get("bungieGlobalDisplayNameCode", None),
370        )

Deserialize a JSON payload of Bungie Developer portal application owner information.

Parameters
Returns
  • aiobungie.crates.application.ApplicationOwner: An application owner.
def deserialize_app( self, payload: dict[str, typing.Any]) -> aiobungie.crates.application.Application:
372    def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application:
373        return application.Application(
374            id=int(payload["applicationId"]),
375            name=payload["name"],
376            link=payload["link"],
377            status=payload["status"],
378            redirect_url=payload.get("redirectUrl", None),
379            created_at=time.clean_date(str(payload["creationDate"])),
380            published_at=time.clean_date(str(payload["firstPublished"])),
381            owner=self.deserialize_app_owner(payload["team"][0]["user"]),  # type: ignore
382            scope=payload.get("scope", undefined.UNDEFINED),
383        )

Deserialize a JSON payload of Bungie Developer portal application information.

Parameters
Returns
  • aiobungie.crates.application.Application: An application.
def deserialize_profile( self, payload: dict[str, typing.Any], /) -> Optional[aiobungie.crates.profile.Profile]:
406    def deserialize_profile(
407        self, payload: typedefs.JSONObject, /
408    ) -> typing.Optional[profile.Profile]:
409        if (raw_profile := payload.get("data")) is None:
410            return None
411
412        payload = raw_profile
413        id = int(payload["userInfo"]["membershipId"])
414        name = payload["userInfo"]["displayName"]
415        is_public = payload["userInfo"]["isPublic"]
416        type = enums.MembershipType(payload["userInfo"]["membershipType"])
417        last_played = time.clean_date(str(payload["dateLastPlayed"]))
418        character_ids = [int(cid) for cid in payload["characterIds"]]
419        power_cap = payload["currentSeasonRewardPowerCap"]
420
421        return profile.Profile(
422            id=int(id),
423            name=name,
424            is_public=is_public,
425            type=type,
426            last_played=last_played,
427            character_ids=character_ids,
428            power_cap=power_cap,
429            net=self._net,
430        )

Deserialize a JSON payload of Bungie.net profile information.

Parameters
Returns
def deserialize_profile_item( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.ProfileItemImpl:
432    def deserialize_profile_item(
433        self, payload: typedefs.JSONObject
434    ) -> profile.ProfileItemImpl:
435
436        instance_id: typing.Optional[int] = None
437        if raw_instance_id := payload.get("itemInstanceId"):
438            instance_id = int(raw_instance_id)
439
440        version_number: typing.Optional[int] = None
441        if raw_version := payload.get("versionNumber"):
442            version_number = int(raw_version)
443
444        transfer_status = enums.TransferStatus(payload["transferStatus"])
445
446        return profile.ProfileItemImpl(
447            net=self._net,
448            hash=payload["itemHash"],
449            quantity=payload["quantity"],
450            bind_status=enums.ItemBindStatus(payload["bindStatus"]),
451            location=enums.ItemLocation(payload["location"]),
452            bucket=payload["bucketHash"],
453            transfer_status=transfer_status,
454            lockable=payload["lockable"],
455            state=enums.ItemState(payload["state"]),
456            dismantel_permissions=payload["dismantlePermission"],
457            is_wrapper=payload["isWrapper"],
458            instance_id=instance_id,
459            version_number=version_number,
460            ornament_id=payload.get("overrideStyleItemHash"),
461        )

Deserialize a JSON payload of a singular profile component item.

Parameters
Returns
def deserialize_objectives( self, payload: dict[str, typing.Any]) -> aiobungie.crates.records.Objective:
463    def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective:
464        return records.Objective(
465            net=self._net,
466            hash=payload["objectiveHash"],
467            visible=payload["visible"],
468            complete=payload["complete"],
469            completion_value=payload["completionValue"],
470            progress=payload.get("progress"),
471            destination_hash=payload.get("destinationHash"),
472            activity_hash=payload.get("activityHash"),
473        )

Deserialize a JSON payload of an objective found in a record profile component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
  • aiobungie.crates.records.Objective: A record objective object.
def deserialize_records( self, payload: dict[str, typing.Any], scores: Optional[aiobungie.crates.records.RecordScores] = None, **nodes: int) -> aiobungie.crates.records.Record:
475    def deserialize_records(
476        self,
477        payload: typedefs.JSONObject,
478        scores: typing.Optional[records.RecordScores] = None,
479        **nodes: int,
480    ) -> records.Record:
481        objectives: typing.Optional[list[records.Objective]] = None
482        interval_objectives: typing.Optional[list[records.Objective]] = None
483        record_state: typedefs.IntAnd[records.RecordState]
484
485        record_state = records.RecordState(payload["state"])
486
487        if raw_objs := payload.get("objectives"):
488            objectives = [self.deserialize_objectives(obj) for obj in raw_objs]
489
490        if raw_interval_objs := payload.get("intervalObjectives"):
491            interval_objectives = [
492                self.deserialize_objectives(obj) for obj in raw_interval_objs
493            ]
494
495        return records.Record(
496            scores=scores,
497            categories_node_hash=nodes.get("categories_hash", undefined.UNDEFINED),
498            seals_node_hash=nodes.get("seals_hash", undefined.UNDEFINED),
499            state=record_state,
500            objectives=objectives,
501            interval_objectives=interval_objectives,
502            redeemed_count=payload.get("intervalsRedeemedCount", 0),
503            completion_times=payload.get("completedCount", None),
504            reward_visibility=payload.get("rewardVisibilty", None),
505        )

Deserialize a JSON object of a profile record component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON object payload
  • scores (typing.Optional[records.RecordScores]): The records scores object. This exists only to keep the signature of aiobungie.crates.CharacterRecord with the record object. As it will always be None in that object.
  • **nodes (int): An int kwargs use to grab the node hashes while deserializing components.
Returns
  • aiobungie.records.Record: A standard implementation of a profile record component.
def deserialize_character_records( self, payload: dict[str, typing.Any], scores: Optional[aiobungie.crates.records.RecordScores] = None, record_hashes: Optional[list[int]] = None) -> aiobungie.crates.records.CharacterRecord:
507    def deserialize_character_records(
508        self,
509        payload: typedefs.JSONObject,
510        scores: typing.Optional[records.RecordScores] = None,
511        record_hashes: typing.Optional[list[int]] = None,
512    ) -> records.CharacterRecord:
513
514        record = self.deserialize_records(payload, scores)
515        return records.CharacterRecord(
516            scores=scores,
517            categories_node_hash=record.categories_node_hash,
518            seals_node_hash=record.seals_node_hash,
519            state=record.state,
520            objectives=record.objectives,
521            interval_objectives=record.interval_objectives,
522            redeemed_count=payload.get("intervalsRedeemedCount", 0),
523            completion_times=payload.get("completedCount"),
524            reward_visibility=payload.get("rewardVisibilty"),
525            record_hashes=record_hashes or [],
526        )

Deserialize a JSON object of a profile character record component.

This almost does the same this as deserialize_records but has more fields which can only be found in a character record.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON object payload
Returns
  • aiobungie.records.CharacterRecord: A standard implementation of a profile character record component.
def deserialize_character_dye(self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.Dye:
528    def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye:
529        return character.Dye(
530            channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"]
531        )

Deserialize a JSON payload of a character's dye information.

Parameters
Returns
  • aiobungie.crates.character.Dye: Information about a character dye object.
def deserialize_character_customization( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.CustomizationOptions:
533    def deserialize_character_customization(
534        self, payload: typedefs.JSONObject
535    ) -> character.CustomizationOptions:
536        return character.CustomizationOptions(
537            personality=payload["personality"],
538            face=payload["face"],
539            skin_color=payload["skinColor"],
540            lip_color=payload["lipColor"],
541            eye_color=payload["eyeColor"],
542            hair_colors=payload.get("hairColors", []),
543            feature_colors=payload.get("featureColors", []),
544            decal_color=payload["decalColor"],
545            wear_helmet=payload["wearHelmet"],
546            hair_index=payload["hairIndex"],
547            feature_index=payload["featureIndex"],
548            decal_index=payload["decalIndex"],
549        )

Deserialize a JSON payload of a character customization information found in character render data profile component.

Parameters
Returns
  • aiobungie.crates.character.CustomizationOptions: Information about a character customs object.
def deserialize_character_minimal_equipments( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.MinimalEquipments:
551    def deserialize_character_minimal_equipments(
552        self, payload: typedefs.JSONObject
553    ) -> character.MinimalEquipments:
554        dyes = None
555        if raw_dyes := payload.get("dyes"):
556            if raw_dyes:
557                dyes = [self.deserialize_character_dye(dye) for dye in raw_dyes]
558        return character.MinimalEquipments(
559            net=self._net, item_hash=payload["itemHash"], dyes=dyes
560        )

Deserialize a singular JSON peer view of equipment found in character render data profile component.

Parameters
Returns
  • aiobungie.crates.character.MinimalEquipments: A minimal equipment object.
def deserialize_character_render_data( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.character.RenderedData:
562    def deserialize_character_render_data(
563        self, payload: typedefs.JSONObject, /
564    ) -> character.RenderedData:
565        return character.RenderedData(
566            net=self._net,
567            customization=self.deserialize_character_customization(
568                payload["customization"]
569            ),
570            custom_dyes=[
571                self.deserialize_character_dye(dye)
572                for dye in payload["customDyes"]
573                if dye
574            ],
575            equipment=[
576                self.deserialize_character_minimal_equipments(equipment)
577                for equipment in payload["peerView"]["equipment"]
578            ],
579        )

Deserialize a JSON payload of a profile character render data component.

Parameters
Returns
def deserialize_available_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.AvailableActivity:
581    def deserialize_available_activity(
582        self, payload: typedefs.JSONObject
583    ) -> activity.AvailableActivity:
584        return activity.AvailableActivity(
585            hash=payload["activityHash"],
586            is_new=payload["isNew"],
587            is_completed=payload["isCompleted"],
588            is_visible=payload["isVisible"],
589            display_level=payload.get("displayLevel"),
590            recommended_light=payload.get("recommendedLight"),
591            difficulty=activity.Difficulty(payload["difficultyTier"]),
592            can_join=payload["canJoin"],
593            can_lead=payload["canLead"],
594        )

Deserialize a JSON payload of an available activities.

This method is used to deserialize an array of aiobungie.crates.CharacterActivity.available_activities.

Parameters
Returns
def deserialize_character_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.CharacterActivity:
596    def deserialize_character_activity(
597        self, payload: typedefs.JSONObject
598    ) -> activity.CharacterActivity:
599        current_mode: typing.Optional[enums.GameMode] = None
600        if raw_current_mode := payload.get("currentActivityModeType"):
601            current_mode = enums.GameMode(raw_current_mode)
602
603        current_mode_types: typing.Optional[collections.Sequence[enums.GameMode]] = None
604        if raw_current_modes := payload.get("currentActivityModeTypes"):
605            current_mode_types = [enums.GameMode(type_) for type_ in raw_current_modes]
606
607        return activity.CharacterActivity(
608            date_started=time.clean_date(payload["dateActivityStarted"]),
609            current_hash=payload["currentActivityHash"],
610            current_mode_hash=payload["currentActivityModeHash"],
611            current_mode=current_mode,
612            current_mode_hashes=payload.get("currentActivityModeHashes"),
613            current_mode_types=current_mode_types,
614            current_playlist_hash=payload.get("currentPlaylistActivityHash"),
615            last_story_hash=payload["lastCompletedStoryHash"],
616            available_activities=[
617                self.deserialize_available_activity(activity_)
618                for activity_ in payload["availableActivities"]
619            ],
620        )

Deserialize a JSON payload of character activity profile component.

Parameters
Returns
def deserialize_profile_items( self, payload: dict[str, typing.Any], /) -> list[aiobungie.crates.profile.ProfileItemImpl]:
622    def deserialize_profile_items(
623        self, payload: typedefs.JSONObject, /
624    ) -> list[profile.ProfileItemImpl]:
625        return [self.deserialize_profile_item(item) for item in payload["items"]]

Deserialize a JSON payload of profile items component information.

This may deserialize profileInventories or profileCurrencies or any other alternatives.

Parameters
Returns
def deserialize_progressions( self, payload: dict[str, typing.Any]) -> aiobungie.crates.progressions.Progression:
668    def deserialize_progressions(
669        self, payload: typedefs.JSONObject
670    ) -> progressions.Progression:
671        return progressions.Progression(
672            hash=int(payload["progressionHash"]),
673            level=int(payload["level"]),
674            cap=int(payload["levelCap"]),
675            daily_limit=int(payload["dailyLimit"]),
676            weekly_limit=int(payload["weeklyLimit"]),
677            current_progress=int(payload["currentProgress"]),
678            daily_progress=int(payload["dailyProgress"]),
679            needed=int(payload["progressToNextLevel"]),
680            next_level=int(payload["nextLevelAt"]),
681        )
def deserialize_milestone( self, payload: dict[str, typing.Any]) -> aiobungie.crates.milestones.Milestone:
769    def deserialize_milestone(
770        self, payload: typedefs.JSONObject
771    ) -> milestones.Milestone:
772        start_date: typing.Optional[datetime.datetime] = None
773        if raw_start_date := payload.get("startDate"):
774            start_date = time.clean_date(raw_start_date)
775
776        end_date: typing.Optional[datetime.datetime] = None
777        if raw_end_date := payload.get("endDate"):
778            end_date = time.clean_date(raw_end_date)
779
780        rewards: typing.Optional[
781            collections.Collection[milestones.MilestoneReward]
782        ] = None
783        if raw_rewards := payload.get("rewards"):
784            rewards = [
785                self._deserialize_milestone_rewards(reward) for reward in raw_rewards
786            ]
787
788        activities: typing.Optional[
789            collections.Sequence[milestones.MilestoneActivity]
790        ] = None
791        if raw_activities := payload.get("activities"):
792            activities = [
793                self._deserialize_milestone_activity(active)
794                for active in raw_activities
795            ]
796
797        quests: typing.Optional[collections.Sequence[milestones.MilestoneQuest]] = None
798        if raw_quests := payload.get("availableQuests"):
799            quests = [
800                self._deserialize_milestone_available_quest(quest)
801                for quest in raw_quests
802            ]
803
804        vendors: typing.Optional[
805            collections.Sequence[milestones.MilestoneVendor]
806        ] = None
807        if raw_vendors := payload.get("vendors"):
808            vendors = [
809                milestones.MilestoneVendor(
810                    vendor_hash=vendor["vendorHash"],
811                    preview_itemhash=vendor.get("previewItemHash"),
812                )
813                for vendor in raw_vendors
814            ]
815
816        return milestones.Milestone(
817            hash=payload["milestoneHash"],
818            start_date=start_date,
819            end_date=end_date,
820            order=payload["order"],
821            rewards=rewards,
822            available_quests=quests,
823            activities=activities,
824            vendors=vendors,
825        )
def deserialize_characters( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.Character]:
842    def deserialize_characters(
843        self, payload: typedefs.JSONObject
844    ) -> collections.Mapping[int, character.Character]:
845        return {
846            int(char_id): self._set_character_attrs(char)
847            for char_id, char in payload["data"].items()
848        }
def deserialize_character( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.Character:
850    def deserialize_character(
851        self, payload: typedefs.JSONObject
852    ) -> character.Character:
853        return self._set_character_attrs(payload)
def deserialize_character_equipments( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, collections.abc.Sequence[aiobungie.crates.profile.ProfileItemImpl]]:
855    def deserialize_character_equipments(
856        self, payload: typedefs.JSONObject
857    ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]:
858        return {
859            int(char_id): self.deserialize_profile_items(item)
860            for char_id, item in payload["data"].items()
861        }
def deserialize_character_activities( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.activity.CharacterActivity]:
863    def deserialize_character_activities(
864        self, payload: typedefs.JSONObject
865    ) -> collections.Mapping[int, activity.CharacterActivity]:
866        return {
867            int(char_id): self.deserialize_character_activity(data)
868            for char_id, data in payload["data"].items()
869        }
def deserialize_characters_render_data( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.RenderedData]:
871    def deserialize_characters_render_data(
872        self, payload: typedefs.JSONObject
873    ) -> collections.Mapping[int, character.RenderedData]:
874        return {
875            int(char_id): self.deserialize_character_render_data(data)
876            for char_id, data in payload["data"].items()
877        }
def deserialize_character_progressions( self, payload: dict[str, typing.Any]) -> aiobungie.crates.character.CharacterProgression:
879    def deserialize_character_progressions(
880        self, payload: typedefs.JSONObject
881    ) -> character.CharacterProgression:
882        progressions_ = {
883            int(prog_id): self.deserialize_progressions(prog)
884            for prog_id, prog in payload["progressions"].items()
885        }
886
887        factions = {
888            int(faction_id): self._deserialize_factions(faction)
889            for faction_id, faction in payload["factions"].items()
890        }
891
892        milestones_ = {
893            int(milestone_hash): self.deserialize_milestone(milestone)
894            for milestone_hash, milestone in payload["milestones"].items()
895        }
896
897        uninstanced_item_objectives = {
898            int(item_hash): [self.deserialize_objectives(ins) for ins in obj]
899            for item_hash, obj in payload["uninstancedItemObjectives"].items()
900        }
901
902        artifact = payload["seasonalArtifact"]
903        seasonal_artifact = season.CharacterScopedArtifact(
904            hash=artifact["artifactHash"],
905            points_used=artifact["pointsUsed"],
906            reset_count=artifact["resetCount"],
907            tiers=[
908                self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"]
909            ],
910        )
911        checklists = payload["checklists"]
912
913        return character.CharacterProgression(
914            progressions=progressions_,
915            factions=factions,
916            checklists=checklists,
917            milestones=milestones_,
918            seasonal_artifact=seasonal_artifact,
919            uninstanced_item_objectives=uninstanced_item_objectives,
920        )
def deserialize_character_progressions_mapping( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.character.CharacterProgression]:
922    def deserialize_character_progressions_mapping(
923        self, payload: typedefs.JSONObject
924    ) -> collections.Mapping[int, character.CharacterProgression]:
925        character_progressions: collections.Mapping[
926            int, character.CharacterProgression
927        ] = {}
928        for char_id, data in payload["data"].items():
929            # A little hack to stop mypy complaining about Mapping <-> dict
930            character_progressions[int(char_id)] = self.deserialize_character_progressions(data)  # type: ignore[index]
931        return character_progressions
def deserialize_characters_records( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.records.CharacterRecord]:
933    def deserialize_characters_records(
934        self,
935        payload: typedefs.JSONObject,
936    ) -> collections.Mapping[int, records.CharacterRecord]:
937
938        return {
939            int(rec_id): self.deserialize_character_records(
940                rec, record_hashes=payload.get("featuredRecordHashes")
941            )
942            for rec_id, rec in payload["records"].items()
943        }
def deserialize_profile_records( self, payload: dict[str, typing.Any]) -> collections.abc.Mapping[int, aiobungie.crates.records.Record]:
945    def deserialize_profile_records(
946        self, payload: typedefs.JSONObject
947    ) -> collections.Mapping[int, records.Record]:
948        raw_profile_records = payload["data"]
949        scores = records.RecordScores(
950            current_score=raw_profile_records["score"],
951            legacy_score=raw_profile_records["legacyScore"],
952            lifetime_score=raw_profile_records["lifetimeScore"],
953        )
954        return {
955            int(record_id): self.deserialize_records(
956                record,
957                scores,
958                categories_hash=raw_profile_records["recordCategoriesRootNodeHash"],
959                seals_hash=raw_profile_records["recordSealsRootNodeHash"],
960            )
961            for record_id, record in raw_profile_records["records"].items()
962        }
def deserialize_craftables_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.CraftablesComponent:
 999    def deserialize_craftables_component(
1000        self, payload: typedefs.JSONObject
1001    ) -> components.CraftablesComponent:
1002        return components.CraftablesComponent(
1003            net=self._net,
1004            craftables={
1005                int(item_id): self._deserialize_craftable_item(item)
1006                for item_id, item in payload["craftables"].items()
1007                if item is not None
1008            },
1009            crafting_root_node_hash=payload["craftingRootNodeHash"],
1010        )
def deserialize_components( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.Component:
1012    def deserialize_components(  # noqa: C901 Too complex.
1013        self, payload: typedefs.JSONObject
1014    ) -> components.Component:
1015
1016        profile_: typing.Optional[profile.Profile] = None
1017        if raw_profile := payload.get("profile"):
1018            profile_ = self.deserialize_profile(raw_profile)
1019
1020        profile_progression: typing.Optional[profile.ProfileProgression] = None
1021        if raw_profile_progression := payload.get("profileProgression"):
1022            profile_progression = self.deserialize_profile_progression(
1023                raw_profile_progression
1024            )
1025
1026        profile_currencies: typing.Optional[
1027            collections.Sequence[profile.ProfileItemImpl]
1028        ] = None
1029        if raw_profile_currencies := payload.get("profileCurrencies"):
1030            if "data" in raw_profile_currencies:
1031                profile_currencies = self.deserialize_profile_items(
1032                    raw_profile_currencies["data"]
1033                )
1034
1035        profile_inventories: typing.Optional[
1036            collections.Sequence[profile.ProfileItemImpl]
1037        ] = None
1038        if raw_profile_inventories := payload.get("profileInventory"):
1039            if "data" in raw_profile_inventories:
1040                profile_inventories = self.deserialize_profile_items(
1041                    raw_profile_inventories["data"]
1042                )
1043
1044        profile_records: typing.Optional[
1045            collections.Mapping[int, records.Record]
1046        ] = None
1047
1048        if raw_profile_records_ := payload.get("profileRecords"):
1049            profile_records = self.deserialize_profile_records(raw_profile_records_)
1050
1051        characters: typing.Optional[typing.Mapping[int, character.Character]] = None
1052        if raw_characters := payload.get("characters"):
1053            characters = self.deserialize_characters(raw_characters)
1054
1055        character_records: typing.Optional[
1056            collections.Mapping[int, records.CharacterRecord]
1057        ] = None
1058
1059        if raw_character_records := payload.get("characterRecords"):
1060            # Had to do it in two steps..
1061            to_update: typedefs.JSONObject = {}
1062            for _, data in raw_character_records["data"].items():
1063                for record_id, record in data.items():
1064                    to_update[record_id] = record
1065
1066            character_records = {
1067                int(rec_id): self.deserialize_character_records(
1068                    rec, record_hashes=to_update.get("featuredRecordHashes")
1069                )
1070                for rec_id, rec in to_update["records"].items()
1071            }
1072
1073        character_equipments: typing.Optional[
1074            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1075        ] = None
1076        if raw_character_equips := payload.get("characterEquipment"):
1077            character_equipments = self.deserialize_character_equipments(
1078                raw_character_equips
1079            )
1080
1081        character_inventories: typing.Optional[
1082            collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]
1083        ] = None
1084        if raw_character_inventories := payload.get("characterInventories"):
1085            if "data" in raw_character_inventories:
1086                character_inventories = self.deserialize_character_equipments(
1087                    raw_character_inventories
1088                )
1089
1090        character_activities: typing.Optional[
1091            collections.Mapping[int, activity.CharacterActivity]
1092        ] = None
1093        if raw_char_acts := payload.get("characterActivities"):
1094            character_activities = self.deserialize_character_activities(raw_char_acts)
1095
1096        character_render_data: typing.Optional[
1097            collections.Mapping[int, character.RenderedData]
1098        ] = None
1099        if raw_character_render_data := payload.get("characterRenderData"):
1100            character_render_data = self.deserialize_characters_render_data(
1101                raw_character_render_data
1102            )
1103
1104        character_progressions: typing.Optional[
1105            collections.Mapping[int, character.CharacterProgression]
1106        ] = None
1107
1108        if raw_character_progressions := payload.get("characterProgressions"):
1109            character_progressions = self.deserialize_character_progressions_mapping(
1110                raw_character_progressions
1111            )
1112
1113        profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None
1114        if raw_profile_string_vars := payload.get("profileStringVariables"):
1115            profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"]
1116
1117        character_string_vars: typing.Optional[
1118            collections.Mapping[int, collections.Mapping[int, int]]
1119        ] = None
1120        if raw_character_string_vars := payload.get("characterStringVariables"):
1121            character_string_vars = {
1122                int(char_id): data["integerValuesByHash"]
1123                for char_id, data in raw_character_string_vars["data"].items()
1124            }
1125
1126        metrics: typing.Optional[
1127            collections.Sequence[
1128                collections.Mapping[
1129                    int, tuple[bool, typing.Optional[records.Objective]]
1130                ]
1131            ]
1132        ] = None
1133        root_node_hash: typing.Optional[int] = None
1134
1135        if raw_metrics := payload.get("metrics"):
1136            root_node_hash = raw_metrics["data"]["metricsRootNodeHash"]
1137            metrics = [
1138                {
1139                    int(metrics_hash): (
1140                        data["invisible"],
1141                        self.deserialize_objectives(data["objectiveProgress"])
1142                        if "objectiveProgress" in data
1143                        else None,
1144                    )
1145                    for metrics_hash, data in raw_metrics["data"]["metrics"].items()
1146                }
1147            ]
1148        transitory: typing.Optional[fireteams.FireteamParty] = None
1149        if raw_transitory := payload.get("profileTransitoryData"):
1150            if "data" in raw_transitory:
1151                transitory = self.deserialize_fireteam_party(raw_transitory["data"])
1152
1153        item_components: typing.Optional[components.ItemsComponent] = None
1154        if raw_item_components := payload.get("itemComponents"):
1155            item_components = self.deserialize_items_component(raw_item_components)
1156
1157        profile_plugsets: typing.Optional[
1158            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1159        ] = None
1160
1161        if raw_profile_plugs := payload.get("profilePlugSets"):
1162            profile_plugsets = {
1163                int(index): [self.deserialize_plug_item_state(state) for state in data]
1164                for index, data in raw_profile_plugs["data"]["plugs"].items()
1165            }
1166
1167        character_plugsets: typing.Optional[
1168            collections.Mapping[
1169                int, collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1170            ]
1171        ] = None
1172        if raw_char_plugsets := payload.get("characterPlugSets"):
1173            character_plugsets = {
1174                int(char_id): {
1175                    int(index): [
1176                        self.deserialize_plug_item_state(state) for state in data
1177                    ]
1178                    for index, data in inner["plugs"].items()
1179                }
1180                for char_id, inner in raw_char_plugsets["data"].items()
1181            }
1182
1183        character_collectibles: typing.Optional[
1184            collections.Mapping[int, items.Collectible]
1185        ] = None
1186        if raw_character_collectibles := payload.get("characterCollectibles"):
1187            character_collectibles = {
1188                int(char_id): self._deserialize_collectible(data)
1189                for char_id, data in raw_character_collectibles["data"].items()
1190            }
1191
1192        profile_collectibles: typing.Optional[items.Collectible] = None
1193        if raw_profile_collectibles := payload.get("profileCollectibles"):
1194            profile_collectibles = self._deserialize_collectible(
1195                raw_profile_collectibles["data"]
1196            )
1197
1198        profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1199        if raw_profile_nodes := payload.get("profilePresentationNodes"):
1200            profile_nodes = {
1201                int(node_hash): self._deserialize_node(node)
1202                for node_hash, node in raw_profile_nodes["data"]["nodes"].items()
1203            }
1204
1205        character_nodes: typing.Optional[
1206            collections.Mapping[int, collections.Mapping[int, records.Node]]
1207        ] = None
1208        if raw_character_nodes := payload.get("characterPresentationNodes"):
1209            character_nodes = {
1210                int(char_id): {
1211                    int(node_hash): self._deserialize_node(node)
1212                    for node_hash, node in each_character["nodes"].items()
1213                }
1214                for char_id, each_character in raw_character_nodes["data"].items()
1215            }
1216
1217        platform_silver: typing.Optional[
1218            collections.Mapping[str, profile.ProfileItemImpl]
1219        ] = None
1220        if raw_platform_silver := payload.get("platformSilver"):
1221            if "data" in raw_platform_silver:
1222                platform_silver = {
1223                    platform_name: self.deserialize_profile_item(item)
1224                    for platform_name, item in raw_platform_silver["data"][
1225                        "platformSilver"
1226                    ].items()
1227                }
1228
1229        character_currency_lookups: typing.Optional[
1230            collections.Mapping[int, collections.Sequence[items.Currency]]
1231        ] = None
1232        if raw_char_lookups := payload.get("characterCurrencyLookups"):
1233            if "data" in raw_char_lookups:
1234                character_currency_lookups = {
1235                    int(char_id): self._deserialize_currencies(currencie)
1236                    for char_id, currencie in raw_char_lookups["data"].items()
1237                }
1238
1239        character_craftables: typing.Optional[
1240            collections.Mapping[int, components.CraftablesComponent]
1241        ] = None
1242        if raw_character_craftables := payload.get("characterCraftables"):
1243
1244            if "data" in raw_character_craftables:
1245                character_craftables = {
1246                    int(char_id): self.deserialize_craftables_component(craftable)
1247                    for char_id, craftable in raw_character_craftables["data"].items()
1248                }
1249
1250        return components.Component(
1251            profiles=profile_,
1252            profile_progression=profile_progression,
1253            profile_currencies=profile_currencies,
1254            profile_inventories=profile_inventories,
1255            profile_records=profile_records,
1256            characters=characters,
1257            character_records=character_records,
1258            character_equipments=character_equipments,
1259            character_inventories=character_inventories,
1260            character_activities=character_activities,
1261            character_render_data=character_render_data,
1262            character_progressions=character_progressions,
1263            profile_string_variables=profile_string_vars,
1264            character_string_variables=character_string_vars,
1265            metrics=metrics,
1266            root_node_hash=root_node_hash,
1267            transitory=transitory,
1268            item_components=item_components,
1269            profile_plugsets=profile_plugsets,
1270            character_plugsets=character_plugsets,
1271            character_collectibles=character_collectibles,
1272            profile_collectibles=profile_collectibles,
1273            profile_nodes=profile_nodes,
1274            character_nodes=character_nodes,
1275            platform_silver=platform_silver,
1276            character_currency_lookups=character_currency_lookups,
1277            character_craftables=character_craftables,
1278        )

Deserialize a JSON payload of Bungie.net profile components information.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_items_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.ItemsComponent:
1280    def deserialize_items_component(
1281        self, payload: typedefs.JSONObject
1282    ) -> components.ItemsComponent:
1283        instances: typing.Optional[
1284            collections.Sequence[collections.Mapping[int, items.ItemInstance]]
1285        ] = None
1286        if raw_instances := payload.get("instances"):
1287            instances = [
1288                {
1289                    int(ins_id): self.deserialize_instanced_item(item)
1290                    for ins_id, item in raw_instances["data"].items()
1291                }
1292            ]
1293
1294        render_data: typing.Optional[
1295            collections.Mapping[int, tuple[bool, dict[int, int]]]
1296        ] = None
1297        if raw_render_data := payload.get("renderData"):
1298            render_data = {
1299                int(ins_id): (data["useCustomDyes"], data["artRegions"])
1300                for ins_id, data in raw_render_data["data"].items()
1301            }
1302
1303        stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None
1304        if raw_stats := payload.get("stats"):
1305            builder: collections.Mapping[int, items.ItemStatsView] = {}
1306            for ins_id, stat in raw_stats["data"].items():
1307                for _, items_ in stat.items():
1308                    builder[int(ins_id)] = self.deserialize_item_stats_view(items_)  # type: ignore[index]
1309            stats = builder
1310
1311        sockets: typing.Optional[
1312            collections.Mapping[int, collections.Sequence[items.ItemSocket]]
1313        ] = None
1314        if raw_sockets := payload.get("sockets"):
1315            sockets = {
1316                int(ins_id): [
1317                    self.deserialize_item_socket(socket) for socket in item["sockets"]
1318                ]
1319                for ins_id, item in raw_sockets["data"].items()
1320            }
1321
1322        objeectives: typing.Optional[
1323            collections.Mapping[int, collections.Sequence[records.Objective]]
1324        ] = None
1325        if raw_objectives := payload.get("objectives"):
1326            objeectives = {
1327                int(ins_id): [self.deserialize_objectives(objective)]
1328                for ins_id, data in raw_objectives["data"].items()
1329                for objective in data["objectives"]
1330            }
1331
1332        perks: typing.Optional[
1333            collections.Mapping[int, collections.Collection[items.ItemPerk]]
1334        ] = None
1335        if raw_perks := payload.get("perks"):
1336            perks = {
1337                int(ins_id): [
1338                    self.deserialize_item_perk(perk) for perk in item["perks"]
1339                ]
1340                for ins_id, item in raw_perks["data"].items()
1341            }
1342
1343        plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None
1344        if raw_plug_states := payload.get("plugStates"):
1345            pending_states: list[items.PlugItemState] = []
1346            for _, plug in raw_plug_states["data"].items():
1347                pending_states.append(self.deserialize_plug_item_state(plug))
1348            plug_states = pending_states
1349
1350        reusable_plugs: typing.Optional[
1351            collections.Mapping[int, collections.Sequence[items.PlugItemState]]
1352        ] = None
1353        if raw_re_plugs := payload.get("reusablePlugs"):
1354            reusable_plugs = {
1355                int(ins_id): [
1356                    self.deserialize_plug_item_state(state) for state in inner
1357                ]
1358                for ins_id, plug in raw_re_plugs["data"].items()
1359                for inner in list(plug["plugs"].values())
1360            }
1361
1362        plug_objectives: typing.Optional[
1363            collections.Mapping[
1364                int, collections.Mapping[int, collections.Collection[records.Objective]]
1365            ]
1366        ] = None
1367        if raw_plug_objectives := payload.get("plugObjectives"):
1368            plug_objectives = {
1369                int(ins_id): {
1370                    int(obj_hash): [self.deserialize_objectives(obj) for obj in objs]
1371                    for obj_hash, objs in inner["objectivesPerPlug"].items()
1372                }
1373                for ins_id, inner in raw_plug_objectives["data"].items()
1374            }
1375
1376        return components.ItemsComponent(
1377            sockets=sockets,
1378            stats=stats,
1379            render_data=render_data,
1380            instances=instances,
1381            objectives=objeectives,
1382            perks=perks,
1383            plug_states=plug_states,
1384            reusable_plugs=reusable_plugs,
1385            plug_objectives=plug_objectives,
1386        )

Deserialize a JSON objects within the itemComponents key.`

def deserialize_character_component( self, payload: dict[str, typing.Any]) -> aiobungie.crates.components.CharacterComponent:
1388    def deserialize_character_component(  # type: ignore[call-arg]
1389        self, payload: typedefs.JSONObject
1390    ) -> components.CharacterComponent:
1391
1392        character_: typing.Optional[character.Character] = None
1393        if raw_singuler_character := payload.get("character"):
1394            character_ = self.deserialize_character(raw_singuler_character["data"])
1395
1396        inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1397        if raw_inventory := payload.get("inventory"):
1398            if "data" in raw_inventory:
1399                inventory = self.deserialize_profile_items(raw_inventory["data"])
1400
1401        activities: typing.Optional[activity.CharacterActivity] = None
1402        if raw_activities := payload.get("activities"):
1403            activities = self.deserialize_character_activity(raw_activities["data"])
1404
1405        equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None
1406        if raw_equipments := payload.get("equipment"):
1407            equipment = self.deserialize_profile_items(raw_equipments["data"])
1408
1409        progressions_: typing.Optional[character.CharacterProgression] = None
1410        if raw_progressions := payload.get("progressions"):
1411            progressions_ = self.deserialize_character_progressions(
1412                raw_progressions["data"]
1413            )
1414
1415        render_data: typing.Optional[character.RenderedData] = None
1416        if raw_render_data := payload.get("renderData"):
1417            render_data = self.deserialize_character_render_data(
1418                raw_render_data["data"]
1419            )
1420
1421        character_records: typing.Optional[
1422            collections.Mapping[int, records.CharacterRecord]
1423        ] = None
1424        if raw_char_records := payload.get("records"):
1425            character_records = self.deserialize_characters_records(
1426                raw_char_records["data"]
1427            )
1428
1429        item_components: typing.Optional[components.ItemsComponent] = None
1430        if raw_item_components := payload.get("itemComponents"):
1431            item_components = self.deserialize_items_component(raw_item_components)
1432
1433        nodes: typing.Optional[collections.Mapping[int, records.Node]] = None
1434        if raw_nodes := payload.get("presentationNodes"):
1435            nodes = {
1436                int(node_hash): self._deserialize_node(node)
1437                for node_hash, node in raw_nodes["data"]["nodes"].items()
1438            }
1439
1440        collectibles: typing.Optional[items.Collectible] = None
1441        if raw_collectibles := payload.get("collectibles"):
1442            collectibles = self._deserialize_collectible(raw_collectibles["data"])
1443
1444        currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None
1445        if raw_currencies := payload.get("currencyLookups"):
1446            if "data" in raw_currencies:
1447                currency_lookups = self._deserialize_currencies(raw_currencies)
1448
1449        return components.CharacterComponent(
1450            activities=activities,
1451            equipment=equipment,
1452            inventory=inventory,
1453            progressions=progressions_,
1454            render_data=render_data,
1455            character=character_,
1456            character_records=character_records,
1457            profile_records=None,
1458            item_components=item_components,
1459            currency_lookups=currency_lookups,
1460            collectibles=collectibles,
1461            nodes=nodes,
1462        )

Deserialize a JSON payload of Destiny 2 character component.

Parameters
Returns
def deserialize_inventory_results( self, payload: dict[str, typing.Any]) -> aiobungie.Iterator[aiobungie.crates.entity.SearchableEntity]:
1490    def deserialize_inventory_results(
1491        self, payload: typedefs.JSONObject
1492    ) -> iterators.Iterator[entity.SearchableEntity]:
1493        suggested_words: list[str] = payload["suggestedWords"]
1494
1495        def _check_unknown(s: str) -> undefined.UndefinedOr[str]:
1496            return s if not typedefs.is_unknown(s) else undefined.UNDEFINED
1497
1498        return iterators.Iterator(
1499            [
1500                entity.SearchableEntity(
1501                    net=self._net,
1502                    hash=data["hash"],
1503                    entity_type=data["entityType"],
1504                    weight=data["weight"],
1505                    suggested_words=suggested_words,
1506                    name=data["displayProperties"]["name"],
1507                    has_icon=data["displayProperties"]["hasIcon"],
1508                    description=_check_unknown(
1509                        data["displayProperties"]["description"]
1510                    ),
1511                    icon=assets.Image(data["displayProperties"]["icon"]),
1512                )
1513                for data in payload["results"]["results"]
1514            ]
1515        )

Deserialize results of searched Destiny2 entities.

Parameters
Returns
def deserialize_inventory_entity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.entity.InventoryEntity:
1544    def deserialize_inventory_entity(  # noqa: C901 Too complex.
1545        self, payload: typedefs.JSONObject, /
1546    ) -> entity.InventoryEntity:
1547
1548        props = self._set_entity_attrs(payload)
1549        objects = self._deserialize_inventory_item_objects(payload)
1550
1551        collectible_hash: typing.Optional[int] = None
1552        if raw_collectible_hash := payload.get("collectibleHash"):
1553            collectible_hash = int(raw_collectible_hash)
1554
1555        secondary_icon: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1556        if raw_second_icon := payload.get("secondaryIcon"):
1557            secondary_icon = assets.Image(raw_second_icon)
1558
1559        secondary_overlay: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1560        if raw_second_overlay := payload.get("secondaryOverlay"):
1561            secondary_overlay = assets.Image(raw_second_overlay)
1562
1563        secondary_special: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1564        if raw_second_special := payload.get("secondarySpecial"):
1565            secondary_special = assets.Image(raw_second_special)
1566
1567        screenshot: undefined.UndefinedOr[assets.Image] = undefined.UNDEFINED
1568        if raw_screenshot := payload.get("screenshot"):
1569            screenshot = assets.Image(raw_screenshot)
1570
1571        watermark_icon: typing.Optional[assets.Image] = None
1572        if raw_watermark_icon := payload.get("iconWatermark"):
1573            watermark_icon = assets.Image(raw_watermark_icon)
1574
1575        watermark_shelved: typing.Optional[assets.Image] = None
1576        if raw_watermark_shelved := payload.get("iconWatermarkShelved"):
1577            watermark_shelved = assets.Image(raw_watermark_shelved)
1578
1579        about: undefined.UndefinedOr[str] = undefined.UNDEFINED
1580        if (raw_about := payload.get("flavorText")) and not typedefs.is_unknown(
1581            raw_about
1582        ):
1583            about = raw_about
1584
1585        ui_item_style: undefined.UndefinedOr[str] = undefined.UNDEFINED
1586        if (
1587            raw_ui_style := payload.get("uiItemDisplayStyle")
1588        ) and not typedefs.is_unknown(raw_ui_style):
1589            ui_item_style = raw_ui_style
1590
1591        tier_and_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1592        if (
1593            raw_tier_and_name := payload.get("itemTypeAndTierDisplayName")
1594        ) and not typedefs.is_unknown(raw_tier_and_name):
1595            tier_and_name = raw_tier_and_name
1596
1597        type_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1598        if (
1599            raw_type_name := payload.get("itemTypeDisplayName")
1600        ) and not typedefs.is_unknown(raw_type_name):
1601            type_name = raw_type_name
1602
1603        display_source: undefined.UndefinedOr[str] = undefined.UNDEFINED
1604        if (
1605            raw_display_source := payload.get("displaySource")
1606        ) and not typedefs.is_unknown(raw_display_source):
1607            display_source = raw_display_source
1608
1609        lorehash: typing.Optional[int] = None
1610        if raw_lore_hash := payload.get("loreHash"):
1611            lorehash = int(raw_lore_hash)
1612
1613        summary_hash: typing.Optional[int] = None
1614        if raw_summary_hash := payload.get("summaryItemHash"):
1615            summary_hash = raw_summary_hash
1616
1617        breaker_type_hash: typing.Optional[int] = None
1618        if raw_breaker_type_hash := payload.get("breakerTypeHash"):
1619            breaker_type_hash = int(raw_breaker_type_hash)
1620
1621        damage_types: typing.Optional[collections.Sequence[int]] = None
1622        if raw_damage_types := payload.get("damageTypes"):
1623            damage_types = [int(type_) for type_ in raw_damage_types]
1624
1625        damagetype_hashes: typing.Optional[collections.Sequence[int]] = None
1626        if raw_damagetype_hashes := payload.get("damageTypeHashes"):
1627            damagetype_hashes = [int(type_) for type_ in raw_damagetype_hashes]
1628
1629        default_damagetype_hash: typing.Optional[int] = None
1630        if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"):
1631            default_damagetype_hash = int(raw_defaultdmg_hash)
1632
1633        emblem_objective_hash: typing.Optional[int] = None
1634        if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"):
1635            emblem_objective_hash = int(raw_emblem_obj_hash)
1636
1637        tier_type: typing.Optional[enums.TierType] = None
1638        tier: typing.Optional[enums.ItemTier] = None
1639        bucket_hash: typing.Optional[int] = None
1640        recovery_hash: typing.Optional[int] = None
1641        tier_name: undefined.UndefinedOr[str] = undefined.UNDEFINED
1642        isinstance_item: bool = False
1643        expire_tool_tip: undefined.UndefinedOr[str] = undefined.UNDEFINED
1644        expire_in_orbit_message: undefined.UndefinedOr[str] = undefined.UNDEFINED
1645        suppress_expiration: bool = False
1646        max_stack_size: typing.Optional[int] = None
1647        stack_label: undefined.UndefinedOr[str] = undefined.UNDEFINED
1648
1649        if inventory := payload.get("inventory"):
1650            tier_type = enums.TierType(int(inventory["tierType"]))
1651            tier = enums.ItemTier(int(inventory["tierTypeHash"]))
1652            bucket_hash = int(inventory["bucketTypeHash"])
1653            recovery_hash = int(inventory["recoveryBucketTypeHash"])
1654            tier_name = inventory["tierTypeName"]
1655            isinstance_item = inventory["isInstanceItem"]
1656            suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"]
1657            max_stack_size = int(inventory["maxStackSize"])
1658
1659            try:
1660                stack_label = inventory["stackUniqueLabel"]
1661            except KeyError:
1662                pass
1663
1664        return entity.InventoryEntity(
1665            net=self._net,
1666            collectible_hash=collectible_hash,
1667            name=props.name,
1668            about=about,
1669            emblem_objective_hash=emblem_objective_hash,
1670            suppress_expiration=suppress_expiration,
1671            max_stack_size=max_stack_size,
1672            stack_label=stack_label,
1673            tier=tier,
1674            tier_type=tier_type,
1675            tier_name=tier_name,
1676            bucket_hash=bucket_hash,
1677            recovery_bucket_hash=recovery_hash,
1678            isinstance_item=isinstance_item,
1679            expire_in_orbit_message=expire_in_orbit_message,
1680            expiration_tooltip=expire_tool_tip,
1681            lore_hash=lorehash,
1682            type_and_tier_name=tier_and_name,
1683            summary_hash=summary_hash,
1684            ui_display_style=ui_item_style,
1685            type_name=type_name,
1686            breaker_type_hash=breaker_type_hash,
1687            description=props.description,
1688            display_source=display_source,
1689            hash=props.hash,
1690            damage_types=damage_types,
1691            index=props.index,
1692            icon=props.icon,
1693            has_icon=props.has_icon,
1694            screenshot=screenshot,
1695            watermark_icon=watermark_icon,
1696            watermark_shelved=watermark_shelved,
1697            secondary_icon=secondary_icon,
1698            secondary_overlay=secondary_overlay,
1699            secondary_special=secondary_special,
1700            type=enums.ItemType(int(payload["itemType"])),
1701            trait_hashes=[int(id_) for id_ in payload.get("traitHashes", [])],
1702            trait_ids=[trait for trait in payload.get("traitIds", [])],
1703            category_hashes=[int(hash_) for hash_ in payload["itemCategoryHashes"]],
1704            item_class=enums.Class(int(payload["classType"])),
1705            sub_type=enums.ItemSubType(int(payload["itemSubType"])),
1706            breaker_type=int(payload["breakerType"]),
1707            default_damagetype=int(payload["defaultDamageType"]),
1708            default_damagetype_hash=default_damagetype_hash,
1709            damagetype_hashes=damagetype_hashes,
1710            tooltip_notifications=payload["tooltipNotifications"],
1711            not_transferable=payload["nonTransferrable"],
1712            allow_actions=payload["allowActions"],
1713            is_equippable=payload["equippable"],
1714            objects=objects,
1715            background_colors=payload.get("backgroundColor", {}),
1716            season_hash=payload.get("seasonHash"),
1717            has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"],
1718        )

Deserialize a JSON payload of an inventory entity item information.

This can be any item from DestinyInventoryItemDefinition definition.

Parameters
Returns
def deserialize_objective_entity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.entity.ObjectiveEntity:
1720    def deserialize_objective_entity(
1721        self, payload: typedefs.JSONObject, /
1722    ) -> entity.ObjectiveEntity:
1723        props = self._set_entity_attrs(payload)
1724        return entity.ObjectiveEntity(
1725            net=self._net,
1726            hash=props.hash,
1727            index=props.index,
1728            description=props.description,
1729            name=props.name,
1730            has_icon=props.has_icon,
1731            icon=props.icon,
1732            unlock_value_hash=payload["unlockValueHash"],
1733            completion_value=payload["completionValue"],
1734            scope=entity.GatingScope(int(payload["scope"])),
1735            location_hash=payload["locationHash"],
1736            allowed_negative_value=payload["allowNegativeValue"],
1737            allowed_value_change=payload["allowValueChangeWhenCompleted"],
1738            counting_downward=payload["isCountingDownward"],
1739            value_style=entity.ValueUIStyle(int(payload["valueStyle"])),
1740            progress_description=payload["progressDescription"],
1741            perks=payload["perks"],
1742            stats=payload["stats"],
1743            minimum_visibility=payload["minimumVisibilityThreshold"],
1744            allow_over_completion=payload["allowOvercompletion"],
1745            show_value_style=payload["showValueOnComplete"],
1746            display_only_objective=payload["isDisplayOnlyObjective"],
1747            complete_value_style=entity.ValueUIStyle(
1748                int(payload["completedValueStyle"])
1749            ),
1750            progress_value_style=entity.ValueUIStyle(
1751                int(payload["inProgressValueStyle"])
1752            ),
1753            ui_label=payload["uiLabel"],
1754            ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])),
1755        )

Deserialize a JSON payload of an objective entity information.

Parameters
Returns
def deserialize_activity( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.activity.Activity:
1783    def deserialize_activity(
1784        self,
1785        payload: typedefs.JSONObject,
1786        /,
1787    ) -> activity.Activity:
1788        period = time.clean_date(payload["period"])
1789        details = payload["activityDetails"]
1790        ref_id = int(details["referenceId"])
1791        instance_id = int(details["instanceId"])
1792        mode = enums.GameMode(details["mode"])
1793        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1794        is_private = details["isPrivate"]
1795        membership_type = enums.MembershipType(int(details["membershipType"]))
1796
1797        # Since we're using the same fields for post activity method
1798        # this check is required since post activity doesn't values values
1799        values = self._deserialize_activity_values(payload["values"])
1800
1801        return activity.Activity(
1802            net=self._net,
1803            hash=ref_id,
1804            instance_id=instance_id,
1805            mode=mode,
1806            modes=modes,
1807            is_private=is_private,
1808            membership_type=membership_type,
1809            occurred_at=period,
1810            values=values,
1811        )

Deserialize a JSON payload of an activity history information.

Parameters
Returns
def deserialize_activities( self, payload: dict[str, typing.Any]) -> aiobungie.Iterator[aiobungie.crates.activity.Activity]:
1813    def deserialize_activities(
1814        self, payload: typedefs.JSONObject
1815    ) -> iterators.Iterator[activity.Activity]:
1816        return iterators.Iterator(
1817            [
1818                self.deserialize_activity(activity_)
1819                for activity_ in payload["activities"]
1820            ]
1821        )

Deserialize a JSON payload of an array of activity history information.

Parameters
Returns
def deserialize_extended_weapon_values( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.ExtendedWeaponValues:
1823    def deserialize_extended_weapon_values(
1824        self, payload: typedefs.JSONObject
1825    ) -> activity.ExtendedWeaponValues:
1826
1827        assists: typing.Optional[int] = None
1828        if raw_assists := payload["values"].get("uniqueWeaponAssists"):
1829            assists = raw_assists["basic"]["value"]
1830        assists_damage: typing.Optional[int] = None
1831
1832        if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"):
1833            assists_damage = raw_assists_damage["basic"]["value"]
1834
1835        return activity.ExtendedWeaponValues(
1836            reference_id=int(payload["referenceId"]),
1837            kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"],
1838            precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][
1839                "value"
1840            ],
1841            assists=assists,
1842            assists_damage=assists_damage,
1843            precision_kills_percentage=(
1844                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"],
1845                payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][
1846                    "displayValue"
1847                ],
1848            ),
1849        )

Deserialize values of extended weapons JSON object.

Parameters
Returns
def deserialize_post_activity_player( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.activity.PostActivityPlayer:
1872    def deserialize_post_activity_player(
1873        self, payload: typedefs.JSONObject, /
1874    ) -> activity.PostActivityPlayer:
1875        player = payload["player"]
1876
1877        class_hash: typedefs.NoneOr[int] = None
1878        if (class_hash := player.get("classHash")) is not None:
1879            class_hash = class_hash
1880
1881        race_hash: typedefs.NoneOr[int] = None
1882        if (race_hash := player.get("raceHash")) is not None:
1883            race_hash = race_hash
1884
1885        gender_hash: typedefs.NoneOr[int] = None
1886        if (gender_hash := player.get("genderHash")) is not None:
1887            gender_hash = gender_hash
1888
1889        character_class: undefined.UndefinedOr[str] = undefined.UNDEFINED
1890        if (
1891            character_class := player.get("characterClass")
1892        ) and not typedefs.is_unknown(character_class):
1893            character_class = character_class
1894
1895        character_level: typedefs.NoneOr[int] = None
1896        if (character_level := player.get("characterLevel")) is not None:
1897            character_level = character_level
1898
1899        return activity.PostActivityPlayer(
1900            standing=int(payload["standing"]),
1901            score=int(payload["score"]["basic"]["value"]),
1902            character_id=payload["characterId"],
1903            destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]),
1904            character_class=character_class,
1905            character_level=character_level,
1906            race_hash=race_hash,
1907            gender_hash=gender_hash,
1908            class_hash=class_hash,
1909            light_level=int(player["lightLevel"]),
1910            emblem_hash=int(player["emblemHash"]),
1911            values=self._deserialize_activity_values(payload["values"]),
1912            extended_values=self._deserialize_extended_values(payload["extended"]),
1913        )

Deserialize a JSON payload of a post activity player information.

Parameters
Returns
def deserialize_post_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.PostActivity:
1925    def deserialize_post_activity(
1926        self, payload: typedefs.JSONObject
1927    ) -> activity.PostActivity:
1928        period = time.clean_date(payload["period"])
1929        details = payload["activityDetails"]
1930        ref_id = int(details["referenceId"])
1931        instance_id = int(details["instanceId"])
1932        mode = enums.GameMode(details["mode"])
1933        modes = [enums.GameMode(int(mode_)) for mode_ in details["modes"]]
1934        is_private = details["isPrivate"]
1935        membership_type = enums.MembershipType(int(details["membershipType"]))
1936        return activity.PostActivity(
1937            net=self._net,
1938            hash=ref_id,
1939            membership_type=membership_type,
1940            instance_id=instance_id,
1941            mode=mode,
1942            modes=modes,
1943            is_private=is_private,
1944            occurred_at=period,
1945            starting_phase=int(payload["startingPhaseIndex"]),
1946            players=[
1947                self.deserialize_post_activity_player(player)
1948                for player in payload["entries"]
1949            ],
1950            teams=[
1951                self._deserialize_post_activity_team(team) for team in payload["teams"]
1952            ],
1953        )

Deserialize a JSON payload of a post activity information.

Parameters
Returns
def deserialize_aggregated_activity( self, payload: dict[str, typing.Any]) -> aiobungie.crates.activity.AggregatedActivity:
1991    def deserialize_aggregated_activity(
1992        self, payload: typedefs.JSONObject
1993    ) -> activity.AggregatedActivity:
1994        return activity.AggregatedActivity(
1995            hash=int(payload["activityHash"]),
1996            values=self._deserialize_aggregated_activity_values(payload["values"]),
1997        )

Deserialize a JSON payload of an aggregated activity.

Parameters
Returns
def deserialize_aggregated_activities( self, payload: dict[str, typing.Any]) -> aiobungie.Iterator[aiobungie.crates.activity.AggregatedActivity]:
1999    def deserialize_aggregated_activities(
2000        self, payload: typedefs.JSONObject
2001    ) -> iterators.Iterator[activity.AggregatedActivity]:
2002        return iterators.Iterator(
2003            [
2004                self.deserialize_aggregated_activity(activity)
2005                for activity in payload["activities"]
2006            ]
2007        )

Deserialize a JSON payload of an array of aggregated activities.

Parameters
Returns
def deserialize_linked_profiles( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.LinkedProfile:
2009    def deserialize_linked_profiles(
2010        self, payload: typedefs.JSONObject
2011    ) -> profile.LinkedProfile:
2012        bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"])
2013        error_profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2014        profiles_vec: typing.MutableSequence[user.DestinyMembership] = []
2015
2016        if raw_profile := payload.get("profiles"):
2017            for pfile in raw_profile:
2018                profiles_vec.append(self.deserialize_destiny_membership(pfile))
2019
2020        if raw_profiles_with_errors := payload.get("profilesWithErrors"):
2021            for raw_error_pfile in raw_profiles_with_errors:
2022                if error_pfile := raw_error_pfile.get("infoCard"):
2023                    error_profiles_vec.append(
2024                        self.deserialize_destiny_membership(error_pfile)
2025                    )
2026
2027        return profile.LinkedProfile(
2028            net=self._net,
2029            bungie=bungie_user,
2030            profiles=profiles_vec,
2031            profiles_with_errors=error_profiles_vec,
2032        )

Deserialize a JSON payload of Bungie.net hard linked profile information.

Parameters
Returns
def deserialize_clan_banners( self, payload: dict[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.clans.ClanBanner]:
2034    def deserialize_clan_banners(
2035        self, payload: typedefs.JSONObject
2036    ) -> collections.Sequence[clans.ClanBanner]:
2037        banners_seq: typing.MutableSequence[clans.ClanBanner] = []
2038        if banners := payload.get("clanBannerDecals"):
2039            for k, v in banners.items():
2040                banner_obj = clans.ClanBanner(
2041                    id=int(k),
2042                    foreground=assets.Image(v["foregroundPath"]),
2043                    background=assets.Image(v["backgroundPath"]),
2044                )
2045                banners_seq.append(banner_obj)
2046        return banners_seq

Deserialize a JSON array of a clan banners information.

Parameters
Returns
def deserialize_public_milestone_content( self, payload: dict[str, typing.Any]) -> aiobungie.crates.milestones.MilestoneContent:
2048    def deserialize_public_milestone_content(
2049        self, payload: typedefs.JSONObject
2050    ) -> milestones.MilestoneContent:
2051        items_categoris: typedefs.NoneOr[milestones.MilestoneItems] = None
2052        if raw_categories := payload.get("itemCategories"):
2053            for item in raw_categories:
2054                title = undefined.UNDEFINED
2055                if raw_title := item.get("title"):
2056                    if raw_title != typedefs.Unknown:
2057                        title = raw_title
2058                if raw_hashes := item.get("itemHashes"):
2059                    hashes: collections.Sequence[int] = raw_hashes
2060
2061                items_categoris = milestones.MilestoneItems(title=title, hashes=hashes)
2062
2063        about = undefined.UNDEFINED
2064        if (raw_about := payload["about"]) != typedefs.Unknown:
2065            about = raw_about
2066
2067        status = undefined.UNDEFINED
2068        if (raw_status := payload["status"]) != typedefs.Unknown:
2069            status = raw_status
2070
2071        tips: typing.MutableSequence[undefined.UndefinedOr[str]] = []
2072        if raw_tips := payload.get("tips"):
2073            for raw_tip in raw_tips:
2074                if raw_tip == typedefs.Unknown:
2075                    raw_tip = undefined.UNDEFINED
2076                tips.append(raw_tip)
2077
2078        return milestones.MilestoneContent(
2079            about=about, status=status, tips=tips, items=items_categoris
2080        )

Deserialize a JSON payload of milestone content information.

Parameters
Returns
def deserialize_friend( self, payload: dict[str, typing.Any], /) -> aiobungie.crates.friends.Friend:
2082    def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend:
2083        name = undefined.UNDEFINED
2084        if (raw_name := payload["bungieGlobalDisplayName"]) != typedefs.Unknown:
2085            name = raw_name
2086
2087        bungie_user: typedefs.NoneOr[user.BungieUser] = None
2088
2089        if raw_bungie_user := payload.get("bungieNetUser"):
2090            bungie_user = self.deserialize_bungie_user(raw_bungie_user)
2091
2092        return friends.Friend(
2093            net=self._net,
2094            id=int(payload["lastSeenAsMembershipId"]),
2095            name=name,
2096            code=payload.get("bungieGlobalDisplayNameCode"),
2097            relationship=enums.Relationship(payload["relationship"]),
2098            user=bungie_user,
2099            online_status=enums.Presence(payload["onlineStatus"]),
2100            online_title=payload["onlineTitle"],
2101            type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]),
2102        )

Deserialize a JSON payload of a Bungie friend information.

Parameters
Returns
def deserialize_friends( self, payload: dict[str, typing.Any]) -> collections.abc.Sequence[aiobungie.crates.friends.Friend]:
2104    def deserialize_friends(
2105        self, payload: typedefs.JSONObject
2106    ) -> collections.Sequence[friends.Friend]:
2107        mut_seq: typing.MutableSequence[friends.Friend] = []
2108        if raw_friends := payload.get("friends"):
2109            for friend in raw_friends:
2110                mut_seq.append(self.deserialize_friend(friend))
2111        return mut_seq

Deserialize a JSON sequence of Bungie friends information.

This is usually used to deserialize the incoming/outgoing friend requests.

Parameters
Returns
def deserialize_friend_requests( self, payload: dict[str, typing.Any]) -> aiobungie.crates.friends.FriendRequestView:
2113    def deserialize_friend_requests(
2114        self, payload: typedefs.JSONObject
2115    ) -> friends.FriendRequestView:
2116        incoming: typing.MutableSequence[friends.Friend] = []
2117        outgoing: typing.MutableSequence[friends.Friend] = []
2118
2119        if raw_incoming_requests := payload.get("incomingRequests"):
2120            for incoming_request in raw_incoming_requests:
2121                incoming.append(self.deserialize_friend(incoming_request))
2122
2123        if raw_outgoing_requests := payload.get("outgoingRequests"):
2124            for outgoing_request in raw_outgoing_requests:
2125                outgoing.append(self.deserialize_friend(outgoing_request))
2126
2127        return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)

Deserialize a JSON sequence of Bungie friend requests information.

This is used for incoming/outgoing friend requests.

Parameters
Returns
def deserialize_fireteams( self, payload: dict[str, typing.Any]) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.Fireteam]]:
2152    def deserialize_fireteams(
2153        self, payload: typedefs.JSONObject
2154    ) -> typedefs.NoneOr[collections.Sequence[fireteams.Fireteam]]:
2155        fireteams_: typing.MutableSequence[fireteams.Fireteam] = []
2156
2157        result: list[typedefs.JSONObject]
2158        if not (result := payload["results"]):
2159            return None
2160        for elem in result:
2161            fireteams_.append(
2162                self._set_fireteam_fields(
2163                    elem, total_results=int(payload["totalResults"])
2164                )
2165            )
2166        return fireteams_

Deserialize a JSON sequence of Bungie fireteams information.

Parameters
Returns
def deserialize_fireteam_destiny_users( self, payload: dict[str, typing.Any]) -> aiobungie.crates.fireteams.FireteamUser:
2168    def deserialize_fireteam_destiny_users(
2169        self, payload: typedefs.JSONObject
2170    ) -> fireteams.FireteamUser:
2171        destiny_obj = self.deserialize_destiny_membership(payload)
2172        # We could helpers.just return a DestinyMembership object but this is
2173        # missing the fireteam display name and id fields.
2174        return fireteams.FireteamUser(
2175            net=self._net,
2176            id=destiny_obj.id,
2177            code=destiny_obj.code,
2178            icon=destiny_obj.icon,
2179            types=destiny_obj.types,
2180            type=destiny_obj.type,
2181            is_public=destiny_obj.is_public,
2182            crossave_override=destiny_obj.crossave_override,
2183            name=destiny_obj.name,
2184            last_seen_name=destiny_obj.last_seen_name,
2185            fireteam_display_name=payload["FireteamDisplayName"],
2186            fireteam_membership_id=enums.MembershipType(
2187                payload["FireteamMembershipType"]
2188            ),
2189        )

Deserialize a JSON payload of Bungie fireteam destiny users information.

Parameters
Returns
def deserialize_fireteam_members( self, payload: dict[str, typing.Any], *, alternatives: bool = False) -> Optional[collections.abc.Sequence[aiobungie.crates.fireteams.FireteamMember]]:
2191    def deserialize_fireteam_members(
2192        self, payload: typedefs.JSONObject, *, alternatives: bool = False
2193    ) -> typing.Optional[collections.Sequence[fireteams.FireteamMember]]:
2194        members_: list[fireteams.FireteamMember] = []
2195        if members := payload.get("Members" if not alternatives else "Alternates"):
2196            for member in members:
2197                bungie_fields = self.deserialize_partial_bungie_user(member)
2198                members_fields = fireteams.FireteamMember(
2199                    destiny_user=self.deserialize_fireteam_destiny_users(member),
2200                    has_microphone=member["hasMicrophone"],
2201                    character_id=int(member["characterId"]),
2202                    date_joined=time.clean_date(member["dateJoined"]),
2203                    last_platform_invite_date=time.clean_date(
2204                        member["lastPlatformInviteAttemptDate"]
2205                    ),
2206                    last_platform_invite_result=int(
2207                        member["lastPlatformInviteAttemptResult"]
2208                    ),
2209                    net=self._net,
2210                    name=bungie_fields.name,
2211                    id=bungie_fields.id,
2212                    icon=bungie_fields.icon,
2213                    is_public=bungie_fields.is_public,
2214                    crossave_override=bungie_fields.crossave_override,
2215                    types=bungie_fields.types,
2216                    type=bungie_fields.type,
2217                )
2218                members_.append(members_fields)
2219        else:
2220            return None
2221        return members_

Deserialize a JSON sequence of Bungie fireteam members information.

Parameters
  • payload (aiobungie.typedefs.JSONObject): The JSON payload.
  • alternatives (bool): If set to True, Then it will deserialize the alternatives data in the payload. If not the it will just deserialize the members data.
Returns
def deserialize_available_fireteams( self, data: dict[str, typing.Any], *, no_results: bool = False) -> Union[aiobungie.crates.fireteams.AvailableFireteam, collections.abc.Sequence[aiobungie.crates.fireteams.AvailableFireteam]]:
2223    def deserialize_available_fireteams(
2224        self,
2225        data: typedefs.JSONObject,
2226        *,
2227        no_results: bool = False,
2228    ) -> typing.Union[
2229        fireteams.AvailableFireteam, collections.Sequence[fireteams.AvailableFireteam]
2230    ]:
2231        fireteams_: list[fireteams.AvailableFireteam] = []
2232
2233        # This needs to be used outside the results
2234        # JSON key.
2235        if no_results is True:
2236            payload = data
2237
2238        if result := payload.get("results"):
2239
2240            for fireteam in result:
2241                found_fireteams = self._set_fireteam_fields(fireteam["Summary"])
2242                fireteams_fields = fireteams.AvailableFireteam(
2243                    id=found_fireteams.id,
2244                    group_id=found_fireteams.group_id,
2245                    platform=found_fireteams.platform,
2246                    activity_type=found_fireteams.activity_type,
2247                    is_immediate=found_fireteams.is_immediate,
2248                    is_public=found_fireteams.is_public,
2249                    is_valid=found_fireteams.is_valid,
2250                    owner_id=found_fireteams.owner_id,
2251                    player_slot_count=found_fireteams.player_slot_count,
2252                    available_player_slots=found_fireteams.available_player_slots,
2253                    available_alternate_slots=found_fireteams.available_alternate_slots,
2254                    title=found_fireteams.title,
2255                    date_created=found_fireteams.date_created,
2256                    locale=found_fireteams.locale,
2257                    last_modified=found_fireteams.last_modified,
2258                    total_results=found_fireteams.total_results,
2259                    members=self.deserialize_fireteam_members(payload),
2260                    alternatives=self.deserialize_fireteam_members(
2261                        payload, alternatives=True
2262                    ),
2263                )
2264            fireteams_.append(fireteams_fields)
2265            if no_results:
2266                return fireteams_fields
2267        return fireteams_

Deserialize a JSON payload of a sequence of/fireteam information.

Parameters
  • payload (aiobungie.typedefs.JSONObject): The JSON payload.
  • no_results (bool): Whether to deserialize the data from results in the payload or not.
Returns
  • typing.Union[aiobungie.crates.fireteams.AvailableFireteam, collections.Sequence[aiobungie.crates.fireteams.AvailableFireteam]] # noqa (E501): An available fireteam or a sequence of available fireteam.
def deserialize_fireteam_party( self, payload: dict[str, typing.Any]) -> aiobungie.crates.fireteams.FireteamParty:
2269    def deserialize_fireteam_party(
2270        self, payload: typedefs.JSONObject
2271    ) -> fireteams.FireteamParty:
2272        last_destination_hash: typing.Optional[int] = None
2273        if raw_dest_hash := payload.get("lastOrbitedDestinationHash"):
2274            last_destination_hash = int(raw_dest_hash)
2275
2276        return fireteams.FireteamParty(
2277            members=[
2278                self._deserialize_fireteam_party_member(member)
2279                for member in payload["partyMembers"]
2280            ],
2281            activity=self._deserialize_fireteam_party_current_activity(
2282                payload["currentActivity"]
2283            ),
2284            settings=self._deserialize_fireteam_party_settings(payload["joinability"]),
2285            last_destination_hash=last_destination_hash,
2286            tracking=payload["tracking"],
2287        )

Deserialize a JSON payload of profileTransitory component response.

Parameters
Returns
def deserialize_seasonal_artifact(self, payload: dict[str, typing.Any]) -> aiobungie.crates.season.Artifact:
2334    def deserialize_seasonal_artifact(
2335        self, payload: typedefs.JSONObject
2336    ) -> season.Artifact:
2337        if raw_artifact := payload.get("seasonalArtifact"):
2338            if points := raw_artifact.get("pointProgression"):
2339                points_prog = progressions.Progression(
2340                    hash=points["progressionHash"],
2341                    level=points["level"],
2342                    cap=points["levelCap"],
2343                    daily_limit=points["dailyLimit"],
2344                    weekly_limit=points["weeklyLimit"],
2345                    current_progress=points["currentProgress"],
2346                    daily_progress=points["dailyProgress"],
2347                    needed=points["progressToNextLevel"],
2348                    next_level=points["nextLevelAt"],
2349                )
2350
2351            if bonus := raw_artifact.get("powerBonusProgression"):
2352                power_bonus_prog = progressions.Progression(
2353                    hash=bonus["progressionHash"],
2354                    level=bonus["level"],
2355                    cap=bonus["levelCap"],
2356                    daily_limit=bonus["dailyLimit"],
2357                    weekly_limit=bonus["weeklyLimit"],
2358                    current_progress=bonus["currentProgress"],
2359                    daily_progress=bonus["dailyProgress"],
2360                    needed=bonus["progressToNextLevel"],
2361                    next_level=bonus["nextLevelAt"],
2362                )
2363            artifact = season.Artifact(
2364                net=self._net,
2365                hash=raw_artifact["artifactHash"],
2366                power_bonus=raw_artifact["powerBonus"],
2367                acquired_points=raw_artifact["pointsAcquired"],
2368                bonus=power_bonus_prog,
2369                points=points_prog,
2370            )
2371        return artifact

Deserialize a JSON payload of a Destiny 2 seasonal artifact information.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_profile_progression( self, payload: dict[str, typing.Any]) -> aiobungie.crates.profile.ProfileProgression:
2373    def deserialize_profile_progression(
2374        self, payload: typedefs.JSONObject
2375    ) -> profile.ProfileProgression:
2376        return profile.ProfileProgression(
2377            artifact=self.deserialize_seasonal_artifact(payload["data"]),
2378            checklist={
2379                int(check_id): checklists
2380                for check_id, checklists in payload["data"]["checklists"].items()
2381            },
2382        )

Deserialize a JSON payload of a profile progression component.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_instanced_item( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemInstance:
2384    def deserialize_instanced_item(
2385        self, payload: typedefs.JSONObject
2386    ) -> items.ItemInstance:
2387        damage_type_hash: typing.Optional[int] = None
2388        if raw_damagetype_hash := payload.get("damageTypeHash"):
2389            damage_type_hash = int(raw_damagetype_hash)
2390
2391        required_hashes: typing.Optional[collections.Collection[int]] = None
2392        if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"):
2393            required_hashes = [int(raw_hash) for raw_hash in raw_required_hashes]
2394
2395        breaker_type: typing.Optional[items.ItemBreakerType] = None
2396        if raw_break_type := payload.get("breakerType"):
2397            breaker_type = items.ItemBreakerType(int(raw_break_type))
2398
2399        breaker_type_hash: typing.Optional[int] = None
2400        if raw_break_type_hash := payload.get("breakerTypeHash"):
2401            breaker_type_hash = int(raw_break_type_hash)
2402
2403        energy: typing.Optional[items.ItemEnergy] = None
2404        if raw_energy := payload.get("energy"):
2405            energy = self.deserialize_item_energy(raw_energy)
2406
2407        primary_stats = None
2408        if raw_primary_stats := payload.get("primaryStat"):
2409            primary_stats = self.deserialize_item_stats_view(raw_primary_stats)
2410
2411        return items.ItemInstance(
2412            damage_type=enums.DamageType(int(payload["damageType"])),
2413            damage_type_hash=damage_type_hash,
2414            primary_stat=primary_stats,
2415            item_level=int(payload["itemLevel"]),
2416            quality=int(payload["quality"]),
2417            is_equipped=payload["isEquipped"],
2418            can_equip=payload["canEquip"],
2419            equip_required_level=int(payload["equipRequiredLevel"]),
2420            required_equip_unlock_hashes=required_hashes,
2421            cant_equip_reason=int(payload["cannotEquipReason"]),
2422            breaker_type=breaker_type,
2423            breaker_type_hash=breaker_type_hash,
2424            energy=energy,
2425        )

Deserialize a JSON object into an instanced item.

Parameters
  • payload (aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
def deserialize_item_energy( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemEnergy:
2427    def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy:
2428        energy_hash: typing.Optional[int] = None
2429        if raw_energy_hash := payload.get("energyTypeHash"):
2430            energy_hash = int(raw_energy_hash)
2431
2432        return items.ItemEnergy(
2433            hash=energy_hash,
2434            type=items.ItemEnergyType(int(payload["energyType"])),
2435            capacity=int(payload["energyCapacity"]),
2436            used_energy=int(payload["energyUsed"]),
2437            unused_energy=int(payload["energyUnused"]),
2438        )
def deserialize_item_perk(self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemPerk:
2440    def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk:
2441        perk_hash: typing.Optional[int] = None
2442        if raw_perk_hash := payload.get("perkHash"):
2443            perk_hash = int(raw_perk_hash)
2444
2445        return items.ItemPerk(
2446            hash=perk_hash,
2447            icon=assets.Image(payload["iconPath"]),
2448            is_active=payload["isActive"],
2449            is_visible=payload["visible"],
2450        )
def deserialize_item_socket( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemSocket:
2452    def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket:
2453        plug_hash: typing.Optional[int] = None
2454        if raw_plug_hash := payload.get("plugHash"):
2455            plug_hash = int(raw_plug_hash)
2456
2457        enable_fail_indexes: typing.Optional[list[int]] = None
2458        if raw_indexes := payload.get("enableFailIndexes"):
2459            enable_fail_indexes = [int(index) for index in raw_indexes]
2460
2461        return items.ItemSocket(
2462            plug_hash=plug_hash,
2463            is_enabled=payload["isEnabled"],
2464            enable_fail_indexes=enable_fail_indexes,
2465            is_visible=payload.get("visible"),
2466        )
def deserialize_item_stats_view( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.ItemStatsView:
2468    def deserialize_item_stats_view(
2469        self, payload: typedefs.JSONObject
2470    ) -> items.ItemStatsView:
2471        return items.ItemStatsView(
2472            stat_hash=payload.get("statHash"), value=payload.get("value")
2473        )
def deserialize_plug_item_state( self, payload: dict[str, typing.Any]) -> aiobungie.crates.items.PlugItemState:
2475    def deserialize_plug_item_state(
2476        self, payload: typedefs.JSONObject
2477    ) -> items.PlugItemState:
2478        item_hash: typing.Optional[int] = None
2479        if raw_item_hash := payload.get("plugItemHash"):
2480            item_hash = int(raw_item_hash)
2481
2482        insert_fail_indexes: typedefs.NoneOr[list[int]] = None
2483        if raw_fail_indexes := payload.get("insertFailIndexes"):
2484            insert_fail_indexes = [int(k) for k in raw_fail_indexes]
2485
2486        enable_fail_indexes: typedefs.NoneOr[list[int]] = None
2487        if raw_enabled_indexes := payload.get("enableFailIndexes"):
2488            enable_fail_indexes = [int(k) for k in raw_enabled_indexes]
2489
2490        return items.PlugItemState(
2491            item_hash=item_hash,
2492            insert_fail_indexes=insert_fail_indexes,
2493            enable_fail_indexes=enable_fail_indexes,
2494            is_enabled=payload["enabled"],
2495            can_insert=payload["canInsert"],
2496        )
@typing.final
class FireteamActivity(builtins.int, aiobungie.Enum):
 67@typing.final
 68class FireteamActivity(int, enums.Enum):
 69    """An enum for the fireteam activities."""
 70
 71    ALL = 0
 72    CRUCIBLE = 2
 73    TRIALS_OF_OSIRIS = 3
 74    NIGHTFALL = 4
 75    ANY = 5
 76    GAMBIT = 6
 77    BLIND_WELL = 7
 78    NIGHTMARE_HUNTS = 12
 79    ALTARS_OF_SORROWS = 14
 80    DUNGEON = 15
 81    RAID_LW = 20
 82    RAID_GOS = 21
 83    RAID_DSC = 22
 84    EXO_CHALLENGE = 23
 85    S12_WRATHBORN = 24
 86    EMPIRE_HUNTS = 25
 87    S13_BATTLEGROUNDS = 26
 88    EXOTIC_QUEST = 27
 89    RAID_VOG = 28
 90    S14_EXPUNGE = 30
 91    S15_ASTRAL_ALIGNMENT = 31
 92    S15_SHATTERED_RELAM = 32
 93    SHATTERED_THRONE = 33
 94    PROPHECY = 34
 95    PIT_OF_HERESY = 35
 96    DOE = 36
 97    """Dares of Eternity."""
 98    DUNGEON_GOA = 37
 99    """Grasp of Avarice."""
100    VOW_OF_THE_DISCPILE = 38
101    CAMPAIGN = 39
102    WELLSPRING = 40
103    S16_BATTLEGROUNDS = 41
104    S17_NIGHTMARE_CONTAINMENT = 44
105    S17_SEVER = 45

An enum for the fireteam activities.

CRUCIBLE = <FireteamActivity.CRUCIBLE: 2>
TRIALS_OF_OSIRIS = <FireteamActivity.TRIALS_OF_OSIRIS: 3>
NIGHTFALL = <FireteamActivity.NIGHTFALL: 4>
GAMBIT = <FireteamActivity.GAMBIT: 6>
BLIND_WELL = <FireteamActivity.BLIND_WELL: 7>
NIGHTMARE_HUNTS = <FireteamActivity.NIGHTMARE_HUNTS: 12>
ALTARS_OF_SORROWS = <FireteamActivity.ALTARS_OF_SORROWS: 14>
DUNGEON = <FireteamActivity.DUNGEON: 15>
RAID_LW = <FireteamActivity.RAID_LW: 20>
RAID_GOS = <FireteamActivity.RAID_GOS: 21>
RAID_DSC = <FireteamActivity.RAID_DSC: 22>
EXO_CHALLENGE = <FireteamActivity.EXO_CHALLENGE: 23>
S12_WRATHBORN = <FireteamActivity.S12_WRATHBORN: 24>
EMPIRE_HUNTS = <FireteamActivity.EMPIRE_HUNTS: 25>
S13_BATTLEGROUNDS = <FireteamActivity.S13_BATTLEGROUNDS: 26>
EXOTIC_QUEST = <FireteamActivity.EXOTIC_QUEST: 27>
RAID_VOG = <FireteamActivity.RAID_VOG: 28>
S14_EXPUNGE = <FireteamActivity.S14_EXPUNGE: 30>
S15_ASTRAL_ALIGNMENT = <FireteamActivity.S15_ASTRAL_ALIGNMENT: 31>
S15_SHATTERED_RELAM = <FireteamActivity.S15_SHATTERED_RELAM: 32>
SHATTERED_THRONE = <FireteamActivity.SHATTERED_THRONE: 33>
PROPHECY = <FireteamActivity.PROPHECY: 34>
PIT_OF_HERESY = <FireteamActivity.PIT_OF_HERESY: 35>
DOE = <FireteamActivity.DOE: 36>

Dares of Eternity.

DUNGEON_GOA = <FireteamActivity.DUNGEON_GOA: 37>

Grasp of Avarice.

VOW_OF_THE_DISCPILE = <FireteamActivity.VOW_OF_THE_DISCPILE: 38>
CAMPAIGN = <FireteamActivity.CAMPAIGN: 39>
WELLSPRING = <FireteamActivity.WELLSPRING: 40>
S16_BATTLEGROUNDS = <FireteamActivity.S16_BATTLEGROUNDS: 41>
S17_NIGHTMARE_CONTAINMENT = <FireteamActivity.S17_NIGHTMARE_CONTAINMENT: 44>
S17_SEVER = <FireteamActivity.S17_SEVER: 45>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class FireteamDate(builtins.int, aiobungie.Enum):
131@typing.final
132class FireteamDate(int, enums.Enum):
133    """An enum for fireteam date ranges."""
134
135    ALL = 0
136    NOW = 1
137    TODAY = 2
138    TWO_DAYS = 3
139    THIS_WEEK = 4

An enum for fireteam date ranges.

ALL = <FireteamDate.ALL: 0>
NOW = <FireteamDate.NOW: 1>
TODAY = <FireteamDate.TODAY: 2>
TWO_DAYS = <FireteamDate.TWO_DAYS: 3>
THIS_WEEK = <FireteamDate.THIS_WEEK: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class FireteamLanguage(builtins.str, aiobungie.Enum):
108@typing.final
109class FireteamLanguage(str, enums.Enum):
110    """An enum for fireteams languages filters."""
111
112    ALL = ""
113    ENGLISH = "en"
114    FRENCH = "fr"
115    ESPANOL = "es"
116    DEUTSCH = "de"
117    ITALIAN = "it"
118    JAPANESE = "ja"
119    PORTUGUESE = "pt-br"
120    RUSSIAN = "ru"
121    POLISH = "pl"
122    KOREAN = "ko"
123    # ? China
124    ZH_CHT = "zh-cht"
125    ZH_CHS = "zh-chs"
126
127    def __str__(self) -> str:
128        return str(self.value)

An enum for fireteams languages filters.

ENGLISH = <FireteamLanguage.ENGLISH: en>
FRENCH = <FireteamLanguage.FRENCH: fr>
ESPANOL = <FireteamLanguage.ESPANOL: es>
DEUTSCH = <FireteamLanguage.DEUTSCH: de>
ITALIAN = <FireteamLanguage.ITALIAN: it>
JAPANESE = <FireteamLanguage.JAPANESE: ja>
PORTUGUESE = <FireteamLanguage.PORTUGUESE: pt-br>
RUSSIAN = <FireteamLanguage.RUSSIAN: ru>
POLISH = <FireteamLanguage.POLISH: pl>
KOREAN = <FireteamLanguage.KOREAN: ko>
ZH_CHT = <FireteamLanguage.ZH_CHT: zh-cht>
ZH_CHS = <FireteamLanguage.ZH_CHS: zh-chs>
Inherited Members
Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@typing.final
class FireteamPlatform(builtins.int, aiobungie.Enum):
54@typing.final
55class FireteamPlatform(int, enums.Enum):
56    """An enum for fireteam related to bungie fireteams.
57    This is different from the normal `aiobungie.MembershipType`.
58    """
59
60    ANY = 0
61    PSN_NETWORK = 1
62    XBOX_LIVE = 2
63    STEAM = 4
64    STADIA = 5

An enum for fireteam related to bungie fireteams. This is different from the normal aiobungie.MembershipType.

PSN_NETWORK = <FireteamPlatform.PSN_NETWORK: 1>
XBOX_LIVE = <FireteamPlatform.XBOX_LIVE: 2>
STEAM = <FireteamPlatform.STEAM: 4>
STADIA = <FireteamPlatform.STADIA: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Flag(enum.Flag):
 97class Flag(__enum.Flag):
 98    """Builtin Python enum flag with extra handlings."""
 99
100    # Needs to type this here for mypy
101    _value_: int
102
103    @property
104    def name(self) -> str:  # type: ignore[override]
105        if self._name_ is None:
106            self._name_ = f"UNKNOWN {self._value_}"
107
108        return self._name_
109
110    @property
111    def value(self) -> int:  # type: ignore[override]
112        return self._value_
113
114    def __str__(self) -> str:
115        return self.name
116
117    def __repr__(self) -> str:
118        return f"<{type(self).__name__}.{self.name}: {self._value_!s}>"
119
120    def __int__(self) -> int:
121        if isinstance(self.value, _ITERABLE):
122            raise TypeError(
123                f"Can't overload {self.value} in {type(self).__name__}, Please use `.value` attribute.",
124            )
125        return int(self.value)
126
127    def __or__(self, other: typing.Union[Flag, int]) -> Flag:
128        return self.__class__(self._value_ | int(other))
129
130    def __xor__(self, other: typing.Union[Flag, int]) -> Flag:
131        return self.__class__(self._value_ ^ int(other))
132
133    def __and__(self, other: typing.Union[Flag, int]) -> Flag:
134        return self.__class__(other & int(other))
135
136    def __invert__(self) -> Flag:
137        return self.__class__(~self._value_)
138
139    def __contains__(self, other: typing.Union[Flag, int]) -> bool:
140        return self.value & int(other) == int(other)

Builtin Python enum flag with extra handlings.

name: str

The name of the Enum member.

value: int

The value of the Enum member.

@attrs.define(auto_exc=True)
class Forbidden(aiobungie.HTTPException):
137@attrs.define(auto_exc=True)
138class Forbidden(HTTPException):
139    """Exception that's raised for when status code 403 occurs."""
140
141    http_status: http.HTTPStatus = attrs.field(
142        default=http.HTTPStatus.FORBIDDEN, init=False
143    )

Exception that's raised for when status code 403 occurs.

Forbidden( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class Forbidden.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
@typing.final
class GameMode(builtins.int, aiobungie.Enum):
269@typing.final
270class GameMode(int, Enum):
271    """An Enum for all available gamemodes in Destiny 2."""
272
273    NONE = 0
274    STORY = 2
275    STRIKE = 3
276    RAID = 4
277    ALLPVP = 5
278    PATROL = 6
279    ALLPVE = 7
280    RESERVED9 = 9
281    CONTROL = 10
282    RESERVED11 = 11
283    CLASH = 12
284    RESERVED13 = 13
285    CRIMSONDOUBLES = 15
286    NIGHTFALL = 16
287    HEROICNIGHTFALL = 17
288    ALLSTRIKES = 18
289    IRONBANNER = 19
290    RESERVED20 = 20
291    RESERVED21 = 21
292    RESERVED22 = 22
293    RESERVED24 = 24
294    ALLMAYHEM = 25
295    RESERVED26 = 26
296    RESERVED27 = 27
297    RESERVED28 = 28
298    RESERVED29 = 29
299    RESERVED30 = 30
300    SUPREMACY = 31
301    PRIVATEMATCHESALL = 32
302    SURVIVAL = 37
303    COUNTDOWN = 38
304    TRIALSOFTHENINE = 39
305    SOCIAL = 40
306    TRIALSCOUNTDOWN = 41
307    TRIALSSURVIVAL = 42
308    IRONBANNERCONTROL = 43
309    IRONBANNERCLASH = 44
310    IRONBANNERSUPREMACY = 45
311    SCOREDNIGHTFALL = 46
312    SCOREDHEROICNIGHTFALL = 47
313    RUMBLE = 48
314    ALLDOUBLES = 49
315    DOUBLES = 50
316    PRIVATEMATCHESCLASH = 51
317    PRIVATEMATCHESCONTROL = 52
318    PRIVATEMATCHESSUPREMACY = 53
319    PRIVATEMATCHESCOUNTDOWN = 54
320    PRIVATEMATCHESSURVIVAL = 55
321    PRIVATEMATCHESMAYHEM = 56
322    PRIVATEMATCHESRUMBLE = 57
323    HEROICADVENTURE = 58
324    SHOWDOWN = 59
325    LOCKDOWN = 60
326    SCORCHED = 61
327    SCORCHEDTEAM = 62
328    GAMBIT = 63
329    ALLPVECOMPETITIVE = 64
330    BREAKTHROUGH = 65
331    BLACKARMORYRUN = 66
332    SALVAGE = 67
333    IRONBANNERSALVAGE = 68
334    PVPCOMPETITIVE = 69
335    PVPQUICKPLAY = 70
336    CLASHQUICKPLAY = 71
337    CLASHCOMPETITIVE = 72
338    CONTROLQUICKPLAY = 73
339    CONTROLCOMPETITIVE = 74
340    GAMBITPRIME = 75
341    RECKONING = 76
342    MENAGERIE = 77
343    VEXOFFENSIVE = 78
344    NIGHTMAREHUNT = 79
345    ELIMINATION = 80
346    MOMENTUM = 81
347    DUNGEON = 82
348    SUNDIAL = 83
349    TRIALS_OF_OSIRIS = 84
350    DARES = 85
351    OFFENSIVE = 86
352    LOSTSECTOR = 87
353    RIFT = 88
354    ZONECONTROL = 89
355    IRONBANNERRIFT = 90

An Enum for all available gamemodes in Destiny 2.

NONE = <GameMode.NONE: 0>
STORY = <GameMode.STORY: 2>
STRIKE = <GameMode.STRIKE: 3>
RAID = <GameMode.RAID: 4>
ALLPVP = <GameMode.ALLPVP: 5>
PATROL = <GameMode.PATROL: 6>
ALLPVE = <GameMode.ALLPVE: 7>
RESERVED9 = <GameMode.RESERVED9: 9>
CONTROL = <GameMode.CONTROL: 10>
RESERVED11 = <GameMode.RESERVED11: 11>
CLASH = <GameMode.CLASH: 12>
RESERVED13 = <GameMode.RESERVED13: 13>
CRIMSONDOUBLES = <GameMode.CRIMSONDOUBLES: 15>
NIGHTFALL = <GameMode.NIGHTFALL: 16>
HEROICNIGHTFALL = <GameMode.HEROICNIGHTFALL: 17>
ALLSTRIKES = <GameMode.ALLSTRIKES: 18>
IRONBANNER = <GameMode.IRONBANNER: 19>
RESERVED20 = <GameMode.RESERVED20: 20>
RESERVED21 = <GameMode.RESERVED21: 21>
RESERVED22 = <GameMode.RESERVED22: 22>
RESERVED24 = <GameMode.RESERVED24: 24>
ALLMAYHEM = <GameMode.ALLMAYHEM: 25>
RESERVED26 = <GameMode.RESERVED26: 26>
RESERVED27 = <GameMode.RESERVED27: 27>
RESERVED28 = <GameMode.RESERVED28: 28>
RESERVED29 = <GameMode.RESERVED29: 29>
RESERVED30 = <GameMode.RESERVED30: 30>
SUPREMACY = <GameMode.SUPREMACY: 31>
PRIVATEMATCHESALL = <GameMode.PRIVATEMATCHESALL: 32>
SURVIVAL = <GameMode.SURVIVAL: 37>
COUNTDOWN = <GameMode.COUNTDOWN: 38>
TRIALSOFTHENINE = <GameMode.TRIALSOFTHENINE: 39>
SOCIAL = <GameMode.SOCIAL: 40>
TRIALSCOUNTDOWN = <GameMode.TRIALSCOUNTDOWN: 41>
TRIALSSURVIVAL = <GameMode.TRIALSSURVIVAL: 42>
IRONBANNERCONTROL = <GameMode.IRONBANNERCONTROL: 43>
IRONBANNERCLASH = <GameMode.IRONBANNERCLASH: 44>
IRONBANNERSUPREMACY = <GameMode.IRONBANNERSUPREMACY: 45>
SCOREDNIGHTFALL = <GameMode.SCOREDNIGHTFALL: 46>
SCOREDHEROICNIGHTFALL = <GameMode.SCOREDHEROICNIGHTFALL: 47>
RUMBLE = <GameMode.RUMBLE: 48>
ALLDOUBLES = <GameMode.ALLDOUBLES: 49>
DOUBLES = <GameMode.DOUBLES: 50>
PRIVATEMATCHESCLASH = <GameMode.PRIVATEMATCHESCLASH: 51>
PRIVATEMATCHESCONTROL = <GameMode.PRIVATEMATCHESCONTROL: 52>
PRIVATEMATCHESSUPREMACY = <GameMode.PRIVATEMATCHESSUPREMACY: 53>
PRIVATEMATCHESCOUNTDOWN = <GameMode.PRIVATEMATCHESCOUNTDOWN: 54>
PRIVATEMATCHESSURVIVAL = <GameMode.PRIVATEMATCHESSURVIVAL: 55>
PRIVATEMATCHESMAYHEM = <GameMode.PRIVATEMATCHESMAYHEM: 56>
PRIVATEMATCHESRUMBLE = <GameMode.PRIVATEMATCHESRUMBLE: 57>
HEROICADVENTURE = <GameMode.HEROICADVENTURE: 58>
SHOWDOWN = <GameMode.SHOWDOWN: 59>
LOCKDOWN = <GameMode.LOCKDOWN: 60>
SCORCHED = <GameMode.SCORCHED: 61>
SCORCHEDTEAM = <GameMode.SCORCHEDTEAM: 62>
GAMBIT = <GameMode.GAMBIT: 63>
ALLPVECOMPETITIVE = <GameMode.ALLPVECOMPETITIVE: 64>
BREAKTHROUGH = <GameMode.BREAKTHROUGH: 65>
BLACKARMORYRUN = <GameMode.BLACKARMORYRUN: 66>
SALVAGE = <GameMode.SALVAGE: 67>
IRONBANNERSALVAGE = <GameMode.IRONBANNERSALVAGE: 68>
PVPCOMPETITIVE = <GameMode.PVPCOMPETITIVE: 69>
PVPQUICKPLAY = <GameMode.PVPQUICKPLAY: 70>
CLASHQUICKPLAY = <GameMode.CLASHQUICKPLAY: 71>
CLASHCOMPETITIVE = <GameMode.CLASHCOMPETITIVE: 72>
CONTROLQUICKPLAY = <GameMode.CONTROLQUICKPLAY: 73>
CONTROLCOMPETITIVE = <GameMode.CONTROLCOMPETITIVE: 74>
GAMBITPRIME = <GameMode.GAMBITPRIME: 75>
RECKONING = <GameMode.RECKONING: 76>
MENAGERIE = <GameMode.MENAGERIE: 77>
VEXOFFENSIVE = <GameMode.VEXOFFENSIVE: 78>
NIGHTMAREHUNT = <GameMode.NIGHTMAREHUNT: 79>
ELIMINATION = <GameMode.ELIMINATION: 80>
MOMENTUM = <GameMode.MOMENTUM: 81>
DUNGEON = <GameMode.DUNGEON: 82>
SUNDIAL = <GameMode.SUNDIAL: 83>
TRIALS_OF_OSIRIS = <GameMode.TRIALS_OF_OSIRIS: 84>
DARES = <GameMode.DARES: 85>
OFFENSIVE = <GameMode.OFFENSIVE: 86>
LOSTSECTOR = <GameMode.LOSTSECTOR: 87>
RIFT = <GameMode.RIFT: 88>
ZONECONTROL = <GameMode.ZONECONTROL: 89>
IRONBANNERRIFT = <GameMode.IRONBANNERRIFT: 90>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class GatingScope(builtins.int, aiobungie.Enum):
58@typing.final
59class GatingScope(int, enums.Enum):
60    """An enum represents restrictive type of gating that is being performed by an entity.
61
62    This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity
63    applies to everyone equally, or to their specific Profile or Character states.
64    """
65
66    NONE = 0
67    GLOBAL = 1
68    CLAN = 2
69    PROFILE = 3
70    CHARACTER = 4
71    ITEM = 5
72    ASSUMED_WORST_CASE = 6

An enum represents restrictive type of gating that is being performed by an entity.

This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity applies to everyone equally, or to their specific Profile or Character states.

NONE = <GatingScope.NONE: 0>
GLOBAL = <GatingScope.GLOBAL: 1>
CLAN = <GatingScope.CLAN: 2>
PROFILE = <GatingScope.PROFILE: 3>
CHARACTER = <GatingScope.CHARACTER: 4>
ITEM = <GatingScope.ITEM: 5>
ASSUMED_WORST_CASE = <GatingScope.ASSUMED_WORST_CASE: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Gender(builtins.int, aiobungie.Enum):
484@typing.final
485class Gender(int, Enum):
486    """An Enum for Destiny Genders."""
487
488    MALE = 0
489    FEMALE = 1
490    UNKNOWN = 2

An Enum for Destiny Genders.

MALE = <Gender.MALE: 0>
FEMALE = <Gender.FEMALE: 1>
UNKNOWN = <Gender.UNKNOWN: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class GroupType(builtins.int, aiobungie.Enum):
653@typing.final
654class GroupType(int, Enum):
655    """An enums for the known bungie group types."""
656
657    GENERAL = 0
658    CLAN = 1

An enums for the known bungie group types.

GENERAL = <GroupType.GENERAL: 0>
CLAN = <GroupType.CLAN: 1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class HTTPError(aiobungie.AiobungieError):
79@attrs.define(auto_exc=True)
80class HTTPError(AiobungieError):
81    """Base HTTP request errors exception."""
82
83    message: str
84    """The error message."""
85
86    http_status: http.HTTPStatus
87    """The response status."""

Base HTTP request errors exception.

HTTPError(message: str, http_status: http.HTTPStatus)
2def __init__(self, message, http_status):
3    self.message = message
4    self.http_status = http_status
5    BaseException.__init__(self, self.message,self.http_status)

Method generated by attrs for class HTTPError.

message: str

The error message.

http_status: http.HTTPStatus

The response status.

Inherited Members
builtins.BaseException
with_traceback
@attrs.define(auto_exc=True, kw_only=True)
class HTTPException(aiobungie.HTTPError):
 90@attrs.define(auto_exc=True, kw_only=True)
 91class HTTPException(HTTPError):
 92    """An in-depth HTTP exception that's raised with more information."""
 93
 94    error_code: int
 95    """The returned Bungie error status code."""
 96
 97    http_status: http.HTTPStatus
 98    """The request response http status."""
 99
100    throttle_seconds: int
101    """The Bungie response throttle seconds."""
102
103    url: typing.Optional[typedefs.StrOrURL]
104    """The URL/endpoint caused this error."""
105
106    body: typing.Any
107    """The response body."""
108
109    headers: multidict.CIMultiDictProxy[str]
110    """The response headers."""
111
112    message: str
113    """A Bungie human readable message describes the cause of the error."""
114
115    error_status: str
116    """A Bungie short error status describes the cause of the error."""
117
118    message_data: dict[str, str]
119    """A dict of string key, value that includes each cause of the error
120    to a message describes information about that error.
121    """
122
123    def __str__(self) -> str:
124        if self.message:
125            message_body = self.message
126
127        if self.error_status:
128            error_status_body = self.error_status
129
130        return (
131            f"{self.http_status.name.replace('_', '').title()} {self.http_status.value}: "
132            f"Error status: {error_status_body}, Error message: {message_body} from {self.url} "
133            f"{str(self.body)}"
134        )

An in-depth HTTP exception that's raised with more information.

HTTPException( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class HTTPException.

error_code: int

The returned Bungie error status code.

http_status: http.HTTPStatus

The request response http status.

throttle_seconds: int

The Bungie response throttle seconds.

url: Union[str, yarl.URL, NoneType]

The URL/endpoint caused this error.

body: Any

The response body.

headers: multidict._multidict.CIMultiDictProxy[str]

The response headers.

message: str

A Bungie human readable message describes the cause of the error.

error_status: str

A Bungie short error status describes the cause of the error.

message_data: dict[str, str]

A dict of string key, value that includes each cause of the error to a message describes information about that error.

Inherited Members
builtins.BaseException
with_traceback
class Image:
 72class Image:
 73    """Representation of an image/avatar/picture at Bungie.
 74
 75    Example
 76    -------
 77    ```py
 78    from aiobungie import Image
 79    img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
 80    print(img)
 81    # https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg
 82
 83    # Stream the image.
 84    async for chunk in img:
 85        # Byte chunks of the image.
 86        print(chunk)
 87
 88    # Save the image to a file.
 89    await img.save("file_name", "/my/path/to/save/to", "jpeg")
 90    ```
 91
 92    Parameters
 93    ----------
 94    path : `str | None`
 95        The path to the image. If `None`, the default missing image path will be used.
 96    """
 97
 98    __slots__ = ("_path",)
 99
100    def __init__(self, path: typing.Optional[str] = None) -> None:
101        self._path = path
102
103    @property
104    def is_missing(self) -> bool:
105        return not self._path
106
107    @property
108    def url(self) -> str:
109        """The URL to the image."""
110        return self.create_url()
111
112    @staticmethod
113    def missing_path() -> str:
114        """Returns the path to the missing Bungie image."""
115        return "img/misc/missing_icon_d2.png"
116
117    def create_url(self) -> str:
118        """Creates a full URL to the image path.
119
120        Returns
121        -------
122        str
123            The URL to the image.
124        """
125        return f"{url.BASE}/{self._path if self._path else self.missing_path()}"
126
127    async def save(
128        self,
129        file_name: str,
130        path: typing.Union[pathlib.Path, str],
131        /,
132        mime_type: typing.Optional[typing.Union[MimeType, str]] = None,
133    ) -> None:
134        """Saves the image to a file.
135
136        Parameters
137        ----------
138        file_name : `str`
139            A name for the file to save the image to.
140        path : `pathlib.Path | str`
141            A path tp save the image to.
142
143        Other Parameters
144        ----------------
145        mime_type : `MimeType | str`
146            Optional MIME type of the image.
147
148        Raises
149        ------
150        `FileNotFoundError`
151            If the path provided does not exist.
152        `RuntimeError`
153            If the image could not be saved.
154        `PermissionError`
155            If the path provided is not writable or does not have write permissions.
156        """
157        if isinstance(path, pathlib.Path) and not path.exists():
158            raise FileNotFoundError(f"File does not exist: {path!r}")
159
160        if self.is_missing:
161            return
162
163        mimetype = mime_type or MimeType.PNG
164        path = pathlib.Path(path)
165
166        loop = helpers.get_or_make_loop()
167        pool = concurrent.futures.ThreadPoolExecutor()
168
169        try:
170            with pool:
171                await loop.run_in_executor(
172                    pool, _write, path, file_name, mimetype, await self.read()
173                )
174                _LOGGER.info("Saved image to %s", file_name)
175
176        except asyncio.CancelledError:
177            pass
178
179        except Exception as err:
180            raise RuntimeError("Encountered an error while saving image.") from err
181
182    async def read(self) -> bytes:
183        """Read this image bytes.
184
185        Returns
186        -------
187        `bytes`
188            The bytes of this image.
189        """
190        client_session = aiohttp.ClientSession()
191
192        try:
193            await client_session.__aenter__()
194            response = await client_session.get(self.create_url())
195
196            if 300 >= response.status >= 200:
197                reader = await response.read()
198
199        except Exception as exc:
200            raise RuntimeError(f"Failed to read image: {exc}") from None
201        finally:
202            await client_session.__aexit__(None, None, None)
203        return reader
204
205    async def iter(self) -> collections.AsyncGenerator[bytes, None]:
206        """Iterates over the image bytes lazily.
207
208        Example
209        -------
210        import aiobungie
211
212        resource = aiobungie.Image("img/misc/missing_icon_d2.png")
213        async for chunk in resource.iter():
214            print(chunk)
215
216        Returns
217        -------
218        `collections.AsyncGenerator[bytes, None]`
219            An async generator of the image bytes.
220        """
221
222        async for chunk in self:
223            yield chunk
224
225    def __repr__(self) -> str:
226        return f"Image(url={self.create_url()})"
227
228    def __str__(self) -> str:
229        return self.create_url()
230
231    def __aiter__(self) -> Image:
232        return self
233
234    async def __anext__(self) -> bytes:
235        return await self.read()
236
237    def __await__(self) -> collections.Generator[None, None, bytes]:
238        return self.__anext__().__await__()

Representation of an image/avatar/picture at Bungie.

Example
from aiobungie import Image
img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
print(img)
# https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg

# Stream the image.
async for chunk in img:
    # Byte chunks of the image.
    print(chunk)

# Save the image to a file.
await img.save("file_name", "/my/path/to/save/to", "jpeg")
Parameters
  • path (str | None): The path to the image. If None, the default missing image path will be used.
Image(path: Optional[str] = None)
100    def __init__(self, path: typing.Optional[str] = None) -> None:
101        self._path = path
url: str

The URL to the image.

@staticmethod
def missing_path() -> str:
112    @staticmethod
113    def missing_path() -> str:
114        """Returns the path to the missing Bungie image."""
115        return "img/misc/missing_icon_d2.png"

Returns the path to the missing Bungie image.

def create_url(self) -> str:
117    def create_url(self) -> str:
118        """Creates a full URL to the image path.
119
120        Returns
121        -------
122        str
123            The URL to the image.
124        """
125        return f"{url.BASE}/{self._path if self._path else self.missing_path()}"

Creates a full URL to the image path.

Returns
  • str: The URL to the image.
async def save( self, file_name: str, path: Union[pathlib.Path, str], /, mime_type: Union[aiobungie.internal.assets.MimeType, str, NoneType] = None) -> None:
127    async def save(
128        self,
129        file_name: str,
130        path: typing.Union[pathlib.Path, str],
131        /,
132        mime_type: typing.Optional[typing.Union[MimeType, str]] = None,
133    ) -> None:
134        """Saves the image to a file.
135
136        Parameters
137        ----------
138        file_name : `str`
139            A name for the file to save the image to.
140        path : `pathlib.Path | str`
141            A path tp save the image to.
142
143        Other Parameters
144        ----------------
145        mime_type : `MimeType | str`
146            Optional MIME type of the image.
147
148        Raises
149        ------
150        `FileNotFoundError`
151            If the path provided does not exist.
152        `RuntimeError`
153            If the image could not be saved.
154        `PermissionError`
155            If the path provided is not writable or does not have write permissions.
156        """
157        if isinstance(path, pathlib.Path) and not path.exists():
158            raise FileNotFoundError(f"File does not exist: {path!r}")
159
160        if self.is_missing:
161            return
162
163        mimetype = mime_type or MimeType.PNG
164        path = pathlib.Path(path)
165
166        loop = helpers.get_or_make_loop()
167        pool = concurrent.futures.ThreadPoolExecutor()
168
169        try:
170            with pool:
171                await loop.run_in_executor(
172                    pool, _write, path, file_name, mimetype, await self.read()
173                )
174                _LOGGER.info("Saved image to %s", file_name)
175
176        except asyncio.CancelledError:
177            pass
178
179        except Exception as err:
180            raise RuntimeError("Encountered an error while saving image.") from err

Saves the image to a file.

Parameters
  • file_name (str): A name for the file to save the image to.
  • path (pathlib.Path | str): A path tp save the image to.
Other Parameters
  • mime_type (MimeType | str): Optional MIME type of the image.
Raises
  • FileNotFoundError: If the path provided does not exist.
  • RuntimeError: If the image could not be saved.
  • PermissionError: If the path provided is not writable or does not have write permissions.
async def read(self) -> bytes:
182    async def read(self) -> bytes:
183        """Read this image bytes.
184
185        Returns
186        -------
187        `bytes`
188            The bytes of this image.
189        """
190        client_session = aiohttp.ClientSession()
191
192        try:
193            await client_session.__aenter__()
194            response = await client_session.get(self.create_url())
195
196            if 300 >= response.status >= 200:
197                reader = await response.read()
198
199        except Exception as exc:
200            raise RuntimeError(f"Failed to read image: {exc}") from None
201        finally:
202            await client_session.__aexit__(None, None, None)
203        return reader

Read this image bytes.

Returns
  • bytes: The bytes of this image.
async def iter(self) -> collections.abc.AsyncGenerator[bytes, None]:
205    async def iter(self) -> collections.AsyncGenerator[bytes, None]:
206        """Iterates over the image bytes lazily.
207
208        Example
209        -------
210        import aiobungie
211
212        resource = aiobungie.Image("img/misc/missing_icon_d2.png")
213        async for chunk in resource.iter():
214            print(chunk)
215
216        Returns
217        -------
218        `collections.AsyncGenerator[bytes, None]`
219            An async generator of the image bytes.
220        """
221
222        async for chunk in self:
223            yield chunk

Iterates over the image bytes lazily.

Example

import aiobungie

resource = aiobungie.Image("img/misc/missing_icon_d2.png") async for chunk in resource.iter(): print(chunk)

Returns
  • collections.AsyncGenerator[bytes, None]: An async generator of the image bytes.
@attrs.define(auto_exc=True)
class InternalServerError(aiobungie.HTTPException):
243@attrs.define(auto_exc=True)
244class InternalServerError(HTTPException):
245    """Raised for 5xx internal server errors."""

Raised for 5xx internal server errors.

InternalServerError( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class InternalServerError.

Inherited Members
HTTPException
error_code
http_status
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
@typing.final
class ItemBindStatus(builtins.int, aiobungie.Enum):
719@typing.final
720class ItemBindStatus(int, Enum):
721    """An enum for Destiny 2 items bind status."""
722
723    NOT_BOUND = 0
724    BOUND_TO_CHARACTER = 1
725    BOUND_TO_ACCOUNT = 2
726    BOUNT_TO_GUILD = 3

An enum for Destiny 2 items bind status.

NOT_BOUND = <ItemBindStatus.NOT_BOUND: 0>
BOUND_TO_CHARACTER = <ItemBindStatus.BOUND_TO_CHARACTER: 1>
BOUND_TO_ACCOUNT = <ItemBindStatus.BOUND_TO_ACCOUNT: 2>
BOUNT_TO_GUILD = <ItemBindStatus.BOUNT_TO_GUILD: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemLocation(builtins.int, aiobungie.Enum):
729@typing.final
730class ItemLocation(int, Enum):
731    """An enum for Destiny 2 items location."""
732
733    UNKNOWN = 0
734    INVENTORY = 1
735    VAULT = 2
736    VENDOR = 3
737    POSTMASTER = 4

An enum for Destiny 2 items location.

UNKNOWN = <ItemLocation.UNKNOWN: 0>
INVENTORY = <ItemLocation.INVENTORY: 1>
VAULT = <ItemLocation.VAULT: 2>
VENDOR = <ItemLocation.VENDOR: 3>
POSTMASTER = <ItemLocation.POSTMASTER: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemState(aiobungie.Flag):
754@typing.final
755class ItemState(Flag):
756    """An enum for Destiny 2 item states."""
757
758    NONE = 0
759    LOCKED = 1 << 0
760    TRACKED = 1 << 1
761    MASTERWORKED = 1 << 2
762    CRAFTED = 1 << 3
763    """If this bit is set, the item has been 'crafted' by the player."""
764    HIGHLITED_OBJECTIVE = 1 << 4
765    """If this bit is set, the item is a 'highlighted' objective."""

An enum for Destiny 2 item states.

NONE = <ItemState.NONE: 0>
LOCKED = <ItemState.LOCKED: 1>
TRACKED = <ItemState.TRACKED: 2>
MASTERWORKED = <ItemState.MASTERWORKED: 4>
CRAFTED = <ItemState.CRAFTED: 8>

If this bit is set, the item has been 'crafted' by the player.

HIGHLITED_OBJECTIVE = <ItemState.HIGHLITED_OBJECTIVE: 16>

If this bit is set, the item is a 'highlighted' objective.

Inherited Members
Flag
name
value
@typing.final
class ItemSubType(builtins.int, aiobungie.Enum):
586@typing.final
587class ItemSubType(int, Enum):
588    """An enum for Destiny 2 inventory items subtype."""
589
590    NONE = 0
591    AUTORIFLE = 6
592    SHOTGUN = 7
593    MACHINEGUN = 8
594    HANDCANNON = 9
595    ROCKETLAUNCHER = 10
596    FUSIONRIFLE = 11
597    SNIPERRIFLE = 12
598    PULSERIFLE = 13
599    SCOUTRIFLE = 14
600    SIDEARM = 17
601    SWORD = 18
602    MASK = 19
603    SHADER = 20
604    ORNAMENT = 21
605    FUSIONRIFLELINE = 22
606    GRENADELAUNCHER = 23
607    SUBMACHINEGUN = 24
608    TRACERIFLE = 25
609    HELMETARMOR = 26
610    GAUNTLETSARMOR = 27
611    CHESTARMOR = 28
612    LEGARMOR = 29
613    CLASSARMOR = 30
614    BOW = 31
615    DUMMYREPEATABLEBOUNTY = 32

An enum for Destiny 2 inventory items subtype.

NONE = <ItemSubType.NONE: 0>
AUTORIFLE = <ItemSubType.AUTORIFLE: 6>
SHOTGUN = <ItemSubType.SHOTGUN: 7>
MACHINEGUN = <ItemSubType.MACHINEGUN: 8>
HANDCANNON = <ItemSubType.HANDCANNON: 9>
ROCKETLAUNCHER = <ItemSubType.ROCKETLAUNCHER: 10>
FUSIONRIFLE = <ItemSubType.FUSIONRIFLE: 11>
SNIPERRIFLE = <ItemSubType.SNIPERRIFLE: 12>
PULSERIFLE = <ItemSubType.PULSERIFLE: 13>
SCOUTRIFLE = <ItemSubType.SCOUTRIFLE: 14>
SIDEARM = <ItemSubType.SIDEARM: 17>
SWORD = <ItemSubType.SWORD: 18>
MASK = <ItemSubType.MASK: 19>
SHADER = <ItemSubType.SHADER: 20>
ORNAMENT = <ItemSubType.ORNAMENT: 21>
FUSIONRIFLELINE = <ItemSubType.FUSIONRIFLELINE: 22>
GRENADELAUNCHER = <ItemSubType.GRENADELAUNCHER: 23>
SUBMACHINEGUN = <ItemSubType.SUBMACHINEGUN: 24>
TRACERIFLE = <ItemSubType.TRACERIFLE: 25>
HELMETARMOR = <ItemSubType.HELMETARMOR: 26>
GAUNTLETSARMOR = <ItemSubType.GAUNTLETSARMOR: 27>
CHESTARMOR = <ItemSubType.CHESTARMOR: 28>
LEGARMOR = <ItemSubType.LEGARMOR: 29>
CLASSARMOR = <ItemSubType.CLASSARMOR: 30>
BOW = <ItemSubType.BOW: 31>
DUMMYREPEATABLEBOUNTY = <ItemSubType.DUMMYREPEATABLEBOUNTY: 32>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemTier(builtins.int, aiobungie.Enum):
618@typing.final
619class ItemTier(int, Enum):
620    """An enum for a Destiny 2 item tier."""
621
622    NONE = 0
623    BASIC = 3340296461
624    COMMON = 2395677314
625    RARE = 2127292149
626    LEGENDERY = 4008398120
627    EXOTIC = 2759499571

An enum for a Destiny 2 item tier.

NONE = <ItemTier.NONE: 0>
BASIC = <ItemTier.BASIC: 3340296461>
COMMON = <ItemTier.COMMON: 2395677314>
RARE = <ItemTier.RARE: 2127292149>
LEGENDERY = <ItemTier.LEGENDERY: 4008398120>
EXOTIC = <ItemTier.EXOTIC: 2759499571>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class ItemType(builtins.int, aiobungie.Enum):
553@typing.final
554class ItemType(int, Enum):
555    """Enums for Destiny2's item types."""
556
557    NONE = 0
558    CURRENCY = 1
559    ARMOR = 2
560    WEAPON = 3
561    MESSAGE = 7
562    ENGRAM = 8
563    CONSUMABLE = 9
564    EXCHANGEMATERIAL = 10
565    MISSIONREWARD = 11
566    QUESTSTEP = 12
567    QUESTSTEPCOMPLETE = 13
568    EMBLEM = 14
569    QUEST = 15
570    SUBCLASS = 16
571    CLANBANNER = 17
572    AURA = 18
573    MOD = 19
574    DUMMY = 20
575    SHIP = 21
576    VEHICLE = 22
577    EMOTE = 23
578    GHOST = 24
579    PACKAGE = 25
580    BOUNTY = 26
581    WRAPPER = 27
582    SEASONALARTIFACT = 28
583    FINISHER = 29

Enums for Destiny2's item types.

NONE = <ItemType.NONE: 0>
CURRENCY = <ItemType.CURRENCY: 1>
ARMOR = <ItemType.ARMOR: 2>
WEAPON = <ItemType.WEAPON: 3>
MESSAGE = <ItemType.MESSAGE: 7>
ENGRAM = <ItemType.ENGRAM: 8>
CONSUMABLE = <ItemType.CONSUMABLE: 9>
EXCHANGEMATERIAL = <ItemType.EXCHANGEMATERIAL: 10>
MISSIONREWARD = <ItemType.MISSIONREWARD: 11>
QUESTSTEP = <ItemType.QUESTSTEP: 12>
QUESTSTEPCOMPLETE = <ItemType.QUESTSTEPCOMPLETE: 13>
EMBLEM = <ItemType.EMBLEM: 14>
QUEST = <ItemType.QUEST: 15>
SUBCLASS = <ItemType.SUBCLASS: 16>
CLANBANNER = <ItemType.CLANBANNER: 17>
AURA = <ItemType.AURA: 18>
MOD = <ItemType.MOD: 19>
DUMMY = <ItemType.DUMMY: 20>
SHIP = <ItemType.SHIP: 21>
VEHICLE = <ItemType.VEHICLE: 22>
EMOTE = <ItemType.EMOTE: 23>
GHOST = <ItemType.GHOST: 24>
PACKAGE = <ItemType.PACKAGE: 25>
BOUNTY = <ItemType.BOUNTY: 26>
WRAPPER = <ItemType.WRAPPER: 27>
SEASONALARTIFACT = <ItemType.SEASONALARTIFACT: 28>
FINISHER = <ItemType.FINISHER: 29>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class Iterator(typing.Generic[~Item]):
 45class Iterator(typing.Generic[Item]):
 46    """A Flat, In-Memory iterator for sequenced based data.
 47
 48    Example
 49    -------
 50    ```py
 51    iterator = Iterator([1, 2, 3])
 52
 53    # Map the results.
 54    for item in iterator.map(lambda item: item * 2):
 55        print(item)
 56    # 2
 57    # 4
 58
 59    # Indexing is also supported.
 60    print(iterator[0])
 61    # 1
 62
 63    # Normal iteration.
 64    for item in iterator:
 65        print(item)
 66    # 1
 67    # 2
 68    # 3
 69
 70    # Union two iterators.
 71    iterator2 = Iterator([4, 5, 6])
 72    final = iterator | iterator2
 73    # <Iterator([1, 2, 3, 4, 5, 6])>
 74    ```
 75
 76    Parameters
 77    ----------
 78    items: `collections.Iterable[Item]`
 79        The items to iterate over.
 80    """
 81
 82    __slots__ = ("_items",)
 83
 84    def __init__(self, items: collections.Iterable[Item]) -> None:
 85        self._items = iter(items)
 86
 87    @typing.overload
 88    def collect(self) -> list[Item]:
 89        ...
 90
 91    @typing.overload
 92    def collect(self, casting: _B) -> list[_B]:
 93        ...
 94
 95    def collect(
 96        self, casting: typing.Optional[_B] = None
 97    ) -> typing.Union[list[Item], list[_B]]:
 98        """Collects all items in the iterator into a list and cast them into an object if provided.
 99
100        Example
101        -------
102        >>> iterator = Iterator([1, 2, 3])
103        >>> iterator.collect(casting=str)
104        ["1", "2", "3"]
105
106        Parameters
107        ----------
108        casting: `T | None`
109            The type to cast the items to. If `None` is provided, the items will be returned as is.
110
111        Raises
112        ------
113        `StopIteration`
114            If no elements are left in the iterator.
115        """
116        if casting is not None:
117            return typing.cast(list[_B], list(map(casting, self._items)))
118
119        return list(self._items)
120
121    def next(self) -> Item:
122        """Returns the next item in the iterator.
123
124        Example
125        -------
126        ```py
127        iterator = Iterator(["1", "2", "3"])
128        item = iterator.next()
129        assert item == "1"
130        item = iterator.next()
131        assert item == "2"
132        ```
133
134        Raises
135        ------
136        `StopIteration`
137            If no elements are left in the iterator.
138        """
139        try:
140            return self.__next__()
141        except StopIteration:
142            self._ok()
143
144    def map(
145        self, predicate: collections.Callable[[Item], OtherItem]
146    ) -> Iterator[OtherItem]:
147        """Maps each item in the iterator to its predicated value.
148
149        Example
150        -------
151        ```py
152        iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
153        print(iterator)
154        # <Iterator([1, 2, 3])>
155        ```
156
157        Parameters
158        ----------
159        predicate: `collections.Callable[[Item], OtherItem]`
160            The function to map each item in the iterator to its predicated value.
161
162        Returns
163        -------
164        `Iterator[OtherItem]`
165            The mapped iterator.
166
167        Raises
168        ------
169        `StopIteration`
170            If no elements are left in the iterator.
171        """
172        return Iterator(map(predicate, self._items))
173
174    def take(self, n: int) -> Iterator[Item]:
175        """Take the first number of items until the number of items are yielded or
176        the end of the iterator is reached.
177
178        Example
179        -------
180        ```py
181        iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
182        print(iterator.take(2))
183        # <Iterator([GameMode.RAID, GameMode.STRIKE])>
184        ```
185
186        Parameters
187        ----------
188        n: `int`
189            The number of items to take.
190
191        Raises
192        ------
193        `StopIteration`
194            If no elements are left in the iterator.
195        """
196        return Iterator(itertools.islice(self._items, n))
197
198    def take_while(
199        self, predicate: collections.Callable[[Item], bool]
200    ) -> Iterator[Item]:
201        """Yields items from the iterator while predicate returns `True`.
202
203        Example
204        -------
205        ```py
206        iterator = Iterator([STEAM, XBOX, STADIA])
207        print(iterator.take_while(lambda platform: platform is not XBOX))
208        # <Iterator([STEAM])>
209        ```
210
211        Parameters
212        ----------
213        predicate: `collections.Callable[[Item], bool]`
214            The function to predicate each item in the iterator.
215
216        Raises
217        ------
218        `StopIteration`
219            If no elements are left in the iterator.
220        """
221        return Iterator(itertools.takewhile(predicate, self._items))
222
223    def drop_while(
224        self, predicate: collections.Callable[[Item], bool]
225    ) -> Iterator[Item]:
226        """Yields items from the iterator while predicate returns `False`.
227
228        Example
229        -------
230        ```py
231        iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
232        print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
233        # <Iterator([DestinyMembership(name="Bob")])>
234        ```
235
236        Parameters
237        ----------
238        predicate: `collections.Callable[[Item], bool]`
239            The function to predicate each item in the iterator.
240
241        Raises
242        ------
243        `StopIteration`
244            If no elements are left in the iterator.
245        """
246        return Iterator(itertools.dropwhile(predicate, self._items))
247
248    def filter(self, predicate: collections.Callable[[Item], bool]) -> Iterator[Item]:
249        """Filters the iterator to only yield items that match the predicate.
250
251        Example
252        -------
253        ```py
254        names = Iterator(["Jim", "Bob", "Mike", "Jess"])
255        print(names.filter(lambda n: n != "Jim"))
256        # <Iterator(["Bob", "Mike", "Jess"])>
257        ```
258        """
259        return Iterator(filter(predicate, self._items))
260
261    def skip(self, n: int) -> Iterator[Item]:
262        """Skips the first number of items in the iterator.
263
264        Example
265        -------
266        ```py
267        iterator = Iterator([STEAM, XBOX, STADIA])
268        print(iterator.skip(1))
269        # <Iterator([XBOX, STADIA])>
270        ```
271        """
272        return Iterator(itertools.islice(self._items, n, None))
273
274    def zip(self, other: Iterator[OtherItem]) -> Iterator[tuple[Item, OtherItem]]:
275        """Zips the iterator with another iterable.
276
277        Example
278        -------
279        ```py
280        iterator = Iterator([1, 3, 5])
281        other = Iterator([2, 4, 6])
282        for item, other_item in iterator.zip(other):
283            print(item, other_item)
284        # <Iterator([(1, 2), (3, 4), (5, 6)])>
285        ```
286
287        Parameters
288        ----------
289        other: `Iterator[OtherItem]`
290            The iterable to zip with.
291
292        Raises
293        ------
294        `StopIteration`
295            If no elements are left in the iterator.
296        """
297        return Iterator(zip(self._items, other))
298
299    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
300        """`True` if all items in the iterator match the predicate.
301
302        Example
303        -------
304        ```py
305        iterator = Iterator([1, 2, 3])
306        while iterator.all(lambda item: isinstance(item, int)):
307            print("Still all integers")
308            continue
309        # Still all integers
310        ```
311
312        Parameters
313        ----------
314        predicate: `collections.Callable[[Item], bool]`
315            The function to test each item in the iterator.
316
317        Raises
318        ------
319        `StopIteration`
320            If no elements are left in the iterator.
321        """
322        return all(predicate(item) for item in self)
323
324    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
325        """`True` if any items in the iterator match the predicate.
326
327        Example
328        -------
329        ```py
330        iterator = Iterator([1, 2, 3])
331        if iterator.any(lambda item: isinstance(item, int)):
332            print("At least one item is an int.")
333        # At least one item is an int.
334        ```
335
336        Parameters
337        ----------
338        predicate: `collections.Callable[[Item], bool]`
339            The function to test each item in the iterator.
340
341        Raises
342        ------
343        `StopIteration`
344            If no elements are left in the iterator.
345        """
346        return any(predicate(item) for item in self)
347
348    def sort(
349        self,
350        *,
351        key: collections.Callable[[Item], typeshed.SupportsRichComparison],
352        reverse: bool = False,
353    ) -> Iterator[Item]:
354        """Sorts the iterator.
355
356        Example
357        -------
358        ```py
359        iterator = Iterator([3, 1, 6, 7])
360        print(iterator.sort(key=lambda item: item))
361        # <Iterator([1, 3, 6, 7])>
362        ```
363
364        Parameters
365        ----------
366        key: `collections.Callable[[Item], Any]`
367            The function to sort by.
368        reverse: `bool`
369            Whether to reverse the sort.
370
371        Raises
372        ------
373        `StopIteration`
374            If no elements are left in the iterator.
375        """
376        return Iterator(sorted(self._items, key=key, reverse=reverse))
377
378    def first(self) -> Item:
379        """Returns the first item in the iterator.
380
381        Example
382        -------
383        ```py
384        iterator = Iterator([3, 1, 6, 7])
385        print(iterator.first())
386        3
387        ```
388
389        Raises
390        ------
391        `StopIteration`
392            If no elements are left in the iterator.
393        """
394        return self.take(1).next()
395
396    def reversed(self) -> Iterator[Item]:
397        """Returns a new iterator that yields the items in the iterator in reverse order.
398
399        Example
400        -------
401        ```py
402        iterator = Iterator([3, 1, 6, 7])
403        print(iterator.reversed())
404        # <Iterator([7, 6, 1, 3])>
405        ```
406
407        Raises
408        ------
409        `StopIteration`
410            If no elements are left in the iterator.
411        """
412        return Iterator(reversed(self.collect()))
413
414    def count(self) -> int:
415        """Returns the number of items in the iterator.
416
417        Example
418        -------
419        ```py
420        iterator = Iterator([3, 1, 6, 7])
421        print(iterator.count())
422        4
423        ```
424        """
425        count = 0
426        for _ in self:
427            count += 1
428
429        return count
430
431    def union(self, other: Iterator[Item]) -> Iterator[Item]:
432        """Returns a new iterator that yields all items from both iterators.
433
434        Example
435        -------
436        ```py
437        iterator = Iterator([1, 2, 3])
438        other = Iterator([4, 5, 6])
439        print(iterator.union(other))
440        # <Iterator([1, 2, 3, 4, 5, 6])>
441        ```
442
443        Parameters
444        ----------
445        other: `Iterator[Item]`
446            The iterable to union with.
447
448        Raises
449        ------
450        `StopIteration`
451            If no elements are left in the iterator.
452        """
453        return Iterator(itertools.chain(self._items, other))
454
455    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
456        """Calls the function on each item in the iterator.
457
458        Example
459        -------
460        ```py
461        iterator = Iterator([1, 2, 3])
462        iterator.for_each(lambda item: print(item))
463        # 1
464        # 2
465        # 3
466        ```
467
468        Parameters
469        ----------
470        func: `typeshed.Callable[[Item], None]`
471            The function to call on each item in the iterator.
472        """
473        for item in self:
474            func(item)
475
476    async def async_for_each(
477        self,
478        func: collections.Callable[[Item], collections.Coroutine[None, None, None]],
479    ) -> None:
480        """Calls the async function on each item in the iterator concurrently.
481
482        Example
483        -------
484        ```py
485        async def signup(username: str) -> None:
486            async with aiohttp.request('POST', '...') as r:
487                # Actual logic.
488                ...
489
490        async def main():
491            users = aiobungie.into_iter(["user_danny", "user_jojo"])
492            await users.async_for_each(lambda username: signup(username))
493        ```
494
495        Parameters
496        ----------
497        func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]`
498            The async function to call on each item in the iterator.
499        """
500        await _helpers.awaits(*(func(item) for item in self))
501
502    def enumerate(self, *, start: int = 0) -> Iterator[tuple[int, Item]]:
503        """Returns a new iterator that yields tuples of the index and item.
504
505        Example
506        -------
507        ```py
508        iterator = Iterator([1, 2, 3])
509        for index, item in iterator.enumerate():
510            print(index, item)
511        # 0 1
512        # 1 2
513        # 2 3
514        ```
515
516        Raises
517        ------
518        `StopIteration`
519            If no elements are left in the iterator.
520        """
521        return Iterator(enumerate(self._items, start=start))
522
523    def _ok(self) -> typing.NoReturn:
524        raise StopIteration("No more items in the iterator.") from None
525
526    def __getitem__(self, index: int) -> Item:
527        try:
528            return self.skip(index).first()
529        except IndexError:
530            self._ok()
531
532    def __or__(self, other: Iterator[Item]) -> Iterator[Item]:
533        return self.union(other)
534
535    # This is a never.
536    def __setitem__(self) -> typing.NoReturn:
537        raise TypeError(
538            f"{type(self).__name__} doesn't support item assignment."
539        ) from None
540
541    def __repr__(self) -> str:
542        return f'<{self.__class__.__name__}({", ".join([str(item) for item in self])})>'
543
544    def __len__(self) -> int:
545        return self.count()
546
547    def __iter__(self) -> Iterator[Item]:
548        return self
549
550    def __next__(self) -> Item:
551        try:
552            item = next(self._items)
553        except StopIteration:
554            self._ok()
555
556        return item

A Flat, In-Memory iterator for sequenced based data.

Example
iterator = Iterator([1, 2, 3])

# Map the results.
for item in iterator.map(lambda item: item * 2):
    print(item)
# 2
# 4

# Indexing is also supported.
print(iterator[0])
# 1

# Normal iteration.
for item in iterator:
    print(item)
# 1
# 2
# 3

# Union two iterators.
iterator2 = Iterator([4, 5, 6])
final = iterator | iterator2
# <Iterator([1, 2, 3, 4, 5, 6])>
Parameters
  • items (collections.Iterable[Item]): The items to iterate over.
Iterator(items: collections.abc.Iterable[~Item])
84    def __init__(self, items: collections.Iterable[Item]) -> None:
85        self._items = iter(items)
def collect( self, casting: 'typing.Optional[_B]' = None) -> 'typing.Union[list[Item], list[_B]]':
 95    def collect(
 96        self, casting: typing.Optional[_B] = None
 97    ) -> typing.Union[list[Item], list[_B]]:
 98        """Collects all items in the iterator into a list and cast them into an object if provided.
 99
100        Example
101        -------
102        >>> iterator = Iterator([1, 2, 3])
103        >>> iterator.collect(casting=str)
104        ["1", "2", "3"]
105
106        Parameters
107        ----------
108        casting: `T | None`
109            The type to cast the items to. If `None` is provided, the items will be returned as is.
110
111        Raises
112        ------
113        `StopIteration`
114            If no elements are left in the iterator.
115        """
116        if casting is not None:
117            return typing.cast(list[_B], list(map(casting, self._items)))
118
119        return list(self._items)

Collects all items in the iterator into a list and cast them into an object if provided.

Example
>>> iterator = Iterator([1, 2, 3])
>>> iterator.collect(casting=str)
["1", "2", "3"]
Parameters
  • casting (T | None): The type to cast the items to. If None is provided, the items will be returned as is.
Raises
  • StopIteration: If no elements are left in the iterator.
def next(self) -> ~Item:
121    def next(self) -> Item:
122        """Returns the next item in the iterator.
123
124        Example
125        -------
126        ```py
127        iterator = Iterator(["1", "2", "3"])
128        item = iterator.next()
129        assert item == "1"
130        item = iterator.next()
131        assert item == "2"
132        ```
133
134        Raises
135        ------
136        `StopIteration`
137            If no elements are left in the iterator.
138        """
139        try:
140            return self.__next__()
141        except StopIteration:
142            self._ok()

Returns the next item in the iterator.

Example
iterator = Iterator(["1", "2", "3"])
item = iterator.next()
assert item == "1"
item = iterator.next()
assert item == "2"
Raises
  • StopIteration: If no elements are left in the iterator.
def map( self, predicate: 'collections.Callable[[Item], OtherItem]') -> 'Iterator[OtherItem]':
144    def map(
145        self, predicate: collections.Callable[[Item], OtherItem]
146    ) -> Iterator[OtherItem]:
147        """Maps each item in the iterator to its predicated value.
148
149        Example
150        -------
151        ```py
152        iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
153        print(iterator)
154        # <Iterator([1, 2, 3])>
155        ```
156
157        Parameters
158        ----------
159        predicate: `collections.Callable[[Item], OtherItem]`
160            The function to map each item in the iterator to its predicated value.
161
162        Returns
163        -------
164        `Iterator[OtherItem]`
165            The mapped iterator.
166
167        Raises
168        ------
169        `StopIteration`
170            If no elements are left in the iterator.
171        """
172        return Iterator(map(predicate, self._items))

Maps each item in the iterator to its predicated value.

Example
iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
print(iterator)
# <Iterator([1, 2, 3])>
Parameters
  • predicate (collections.Callable[[Item], OtherItem]): The function to map each item in the iterator to its predicated value.
Returns
  • Iterator[OtherItem]: The mapped iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def take(self, n: int) -> aiobungie.Iterator[~Item]:
174    def take(self, n: int) -> Iterator[Item]:
175        """Take the first number of items until the number of items are yielded or
176        the end of the iterator is reached.
177
178        Example
179        -------
180        ```py
181        iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
182        print(iterator.take(2))
183        # <Iterator([GameMode.RAID, GameMode.STRIKE])>
184        ```
185
186        Parameters
187        ----------
188        n: `int`
189            The number of items to take.
190
191        Raises
192        ------
193        `StopIteration`
194            If no elements are left in the iterator.
195        """
196        return Iterator(itertools.islice(self._items, n))

Take the first number of items until the number of items are yielded or the end of the iterator is reached.

Example
iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
print(iterator.take(2))
# <Iterator([GameMode.RAID, GameMode.STRIKE])>
Parameters
  • n (int): The number of items to take.
Raises
  • StopIteration: If no elements are left in the iterator.
def take_while( self, predicate: collections.abc.Callable[[~Item], bool]) -> aiobungie.Iterator[~Item]:
198    def take_while(
199        self, predicate: collections.Callable[[Item], bool]
200    ) -> Iterator[Item]:
201        """Yields items from the iterator while predicate returns `True`.
202
203        Example
204        -------
205        ```py
206        iterator = Iterator([STEAM, XBOX, STADIA])
207        print(iterator.take_while(lambda platform: platform is not XBOX))
208        # <Iterator([STEAM])>
209        ```
210
211        Parameters
212        ----------
213        predicate: `collections.Callable[[Item], bool]`
214            The function to predicate each item in the iterator.
215
216        Raises
217        ------
218        `StopIteration`
219            If no elements are left in the iterator.
220        """
221        return Iterator(itertools.takewhile(predicate, self._items))

Yields items from the iterator while predicate returns True.

Example
iterator = Iterator([STEAM, XBOX, STADIA])
print(iterator.take_while(lambda platform: platform is not XBOX))
# <Iterator([STEAM])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def drop_while( self, predicate: collections.abc.Callable[[~Item], bool]) -> aiobungie.Iterator[~Item]:
223    def drop_while(
224        self, predicate: collections.Callable[[Item], bool]
225    ) -> Iterator[Item]:
226        """Yields items from the iterator while predicate returns `False`.
227
228        Example
229        -------
230        ```py
231        iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
232        print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
233        # <Iterator([DestinyMembership(name="Bob")])>
234        ```
235
236        Parameters
237        ----------
238        predicate: `collections.Callable[[Item], bool]`
239            The function to predicate each item in the iterator.
240
241        Raises
242        ------
243        `StopIteration`
244            If no elements are left in the iterator.
245        """
246        return Iterator(itertools.dropwhile(predicate, self._items))

Yields items from the iterator while predicate returns False.

Example
iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
# <Iterator([DestinyMembership(name="Bob")])>
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def filter( self, predicate: collections.abc.Callable[[~Item], bool]) -> aiobungie.Iterator[~Item]:
248    def filter(self, predicate: collections.Callable[[Item], bool]) -> Iterator[Item]:
249        """Filters the iterator to only yield items that match the predicate.
250
251        Example
252        -------
253        ```py
254        names = Iterator(["Jim", "Bob", "Mike", "Jess"])
255        print(names.filter(lambda n: n != "Jim"))
256        # <Iterator(["Bob", "Mike", "Jess"])>
257        ```
258        """
259        return Iterator(filter(predicate, self._items))

Filters the iterator to only yield items that match the predicate.

Example
names = Iterator(["Jim", "Bob", "Mike", "Jess"])
print(names.filter(lambda n: n != "Jim"))
# <Iterator(["Bob", "Mike", "Jess"])>
def skip(self, n: int) -> aiobungie.Iterator[~Item]:
261    def skip(self, n: int) -> Iterator[Item]:
262        """Skips the first number of items in the iterator.
263
264        Example
265        -------
266        ```py
267        iterator = Iterator([STEAM, XBOX, STADIA])
268        print(iterator.skip(1))
269        # <Iterator([XBOX, STADIA])>
270        ```
271        """
272        return Iterator(itertools.islice(self._items, n, None))

Skips the first number of items in the iterator.

Example
iterator = Iterator([STEAM, XBOX, STADIA])
print(iterator.skip(1))
# <Iterator([XBOX, STADIA])>
def zip(self, other: 'Iterator[OtherItem]') -> 'Iterator[tuple[Item, OtherItem]]':
274    def zip(self, other: Iterator[OtherItem]) -> Iterator[tuple[Item, OtherItem]]:
275        """Zips the iterator with another iterable.
276
277        Example
278        -------
279        ```py
280        iterator = Iterator([1, 3, 5])
281        other = Iterator([2, 4, 6])
282        for item, other_item in iterator.zip(other):
283            print(item, other_item)
284        # <Iterator([(1, 2), (3, 4), (5, 6)])>
285        ```
286
287        Parameters
288        ----------
289        other: `Iterator[OtherItem]`
290            The iterable to zip with.
291
292        Raises
293        ------
294        `StopIteration`
295            If no elements are left in the iterator.
296        """
297        return Iterator(zip(self._items, other))

Zips the iterator with another iterable.

Example
iterator = Iterator([1, 3, 5])
other = Iterator([2, 4, 6])
for item, other_item in iterator.zip(other):
    print(item, other_item)
# <Iterator([(1, 2), (3, 4), (5, 6)])>
Parameters
  • other (Iterator[OtherItem]): The iterable to zip with.
Raises
  • StopIteration: If no elements are left in the iterator.
def all(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
299    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
300        """`True` if all items in the iterator match the predicate.
301
302        Example
303        -------
304        ```py
305        iterator = Iterator([1, 2, 3])
306        while iterator.all(lambda item: isinstance(item, int)):
307            print("Still all integers")
308            continue
309        # Still all integers
310        ```
311
312        Parameters
313        ----------
314        predicate: `collections.Callable[[Item], bool]`
315            The function to test each item in the iterator.
316
317        Raises
318        ------
319        `StopIteration`
320            If no elements are left in the iterator.
321        """
322        return all(predicate(item) for item in self)

True if all items in the iterator match the predicate.

Example
iterator = Iterator([1, 2, 3])
while iterator.all(lambda item: isinstance(item, int)):
    print("Still all integers")
    continue
# Still all integers
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def any(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
324    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
325        """`True` if any items in the iterator match the predicate.
326
327        Example
328        -------
329        ```py
330        iterator = Iterator([1, 2, 3])
331        if iterator.any(lambda item: isinstance(item, int)):
332            print("At least one item is an int.")
333        # At least one item is an int.
334        ```
335
336        Parameters
337        ----------
338        predicate: `collections.Callable[[Item], bool]`
339            The function to test each item in the iterator.
340
341        Raises
342        ------
343        `StopIteration`
344            If no elements are left in the iterator.
345        """
346        return any(predicate(item) for item in self)

True if any items in the iterator match the predicate.

Example
iterator = Iterator([1, 2, 3])
if iterator.any(lambda item: isinstance(item, int)):
    print("At least one item is an int.")
# At least one item is an int.
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
  • StopIteration: If no elements are left in the iterator.
def sort( self, *, key: 'collections.Callable[[Item], typeshed.SupportsRichComparison]', reverse: bool = False) -> aiobungie.Iterator[~Item]:
348    def sort(
349        self,
350        *,
351        key: collections.Callable[[Item], typeshed.SupportsRichComparison],
352        reverse: bool = False,
353    ) -> Iterator[Item]:
354        """Sorts the iterator.
355
356        Example
357        -------
358        ```py
359        iterator = Iterator([3, 1, 6, 7])
360        print(iterator.sort(key=lambda item: item))
361        # <Iterator([1, 3, 6, 7])>
362        ```
363
364        Parameters
365        ----------
366        key: `collections.Callable[[Item], Any]`
367            The function to sort by.
368        reverse: `bool`
369            Whether to reverse the sort.
370
371        Raises
372        ------
373        `StopIteration`
374            If no elements are left in the iterator.
375        """
376        return Iterator(sorted(self._items, key=key, reverse=reverse))

Sorts the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.sort(key=lambda item: item))
# <Iterator([1, 3, 6, 7])>
Parameters
  • key (collections.Callable[[Item], Any]): The function to sort by.
  • reverse (bool): Whether to reverse the sort.
Raises
  • StopIteration: If no elements are left in the iterator.
def first(self) -> ~Item:
378    def first(self) -> Item:
379        """Returns the first item in the iterator.
380
381        Example
382        -------
383        ```py
384        iterator = Iterator([3, 1, 6, 7])
385        print(iterator.first())
386        3
387        ```
388
389        Raises
390        ------
391        `StopIteration`
392            If no elements are left in the iterator.
393        """
394        return self.take(1).next()

Returns the first item in the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.first())
3
Raises
  • StopIteration: If no elements are left in the iterator.
def reversed(self) -> aiobungie.Iterator[~Item]:
396    def reversed(self) -> Iterator[Item]:
397        """Returns a new iterator that yields the items in the iterator in reverse order.
398
399        Example
400        -------
401        ```py
402        iterator = Iterator([3, 1, 6, 7])
403        print(iterator.reversed())
404        # <Iterator([7, 6, 1, 3])>
405        ```
406
407        Raises
408        ------
409        `StopIteration`
410            If no elements are left in the iterator.
411        """
412        return Iterator(reversed(self.collect()))

Returns a new iterator that yields the items in the iterator in reverse order.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.reversed())
# <Iterator([7, 6, 1, 3])>
Raises
  • StopIteration: If no elements are left in the iterator.
def count(self) -> int:
414    def count(self) -> int:
415        """Returns the number of items in the iterator.
416
417        Example
418        -------
419        ```py
420        iterator = Iterator([3, 1, 6, 7])
421        print(iterator.count())
422        4
423        ```
424        """
425        count = 0
426        for _ in self:
427            count += 1
428
429        return count

Returns the number of items in the iterator.

Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.count())
4
def union( self, other: aiobungie.Iterator[~Item]) -> aiobungie.Iterator[~Item]:
431    def union(self, other: Iterator[Item]) -> Iterator[Item]:
432        """Returns a new iterator that yields all items from both iterators.
433
434        Example
435        -------
436        ```py
437        iterator = Iterator([1, 2, 3])
438        other = Iterator([4, 5, 6])
439        print(iterator.union(other))
440        # <Iterator([1, 2, 3, 4, 5, 6])>
441        ```
442
443        Parameters
444        ----------
445        other: `Iterator[Item]`
446            The iterable to union with.
447
448        Raises
449        ------
450        `StopIteration`
451            If no elements are left in the iterator.
452        """
453        return Iterator(itertools.chain(self._items, other))

Returns a new iterator that yields all items from both iterators.

Example
iterator = Iterator([1, 2, 3])
other = Iterator([4, 5, 6])
print(iterator.union(other))
# <Iterator([1, 2, 3, 4, 5, 6])>
Parameters
  • other (Iterator[Item]): The iterable to union with.
Raises
  • StopIteration: If no elements are left in the iterator.
def for_each(self, func: collections.abc.Callable[[~Item], typing.Any]) -> None:
455    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
456        """Calls the function on each item in the iterator.
457
458        Example
459        -------
460        ```py
461        iterator = Iterator([1, 2, 3])
462        iterator.for_each(lambda item: print(item))
463        # 1
464        # 2
465        # 3
466        ```
467
468        Parameters
469        ----------
470        func: `typeshed.Callable[[Item], None]`
471            The function to call on each item in the iterator.
472        """
473        for item in self:
474            func(item)

Calls the function on each item in the iterator.

Example
iterator = Iterator([1, 2, 3])
iterator.for_each(lambda item: print(item))
# 1
# 2
# 3
Parameters
  • func (typeshed.Callable[[Item], None]): The function to call on each item in the iterator.
async def async_for_each( self, func: collections.abc.Callable[[~Item], collections.abc.Coroutine[None, None, None]]) -> None:
476    async def async_for_each(
477        self,
478        func: collections.Callable[[Item], collections.Coroutine[None, None, None]],
479    ) -> None:
480        """Calls the async function on each item in the iterator concurrently.
481
482        Example
483        -------
484        ```py
485        async def signup(username: str) -> None:
486            async with aiohttp.request('POST', '...') as r:
487                # Actual logic.
488                ...
489
490        async def main():
491            users = aiobungie.into_iter(["user_danny", "user_jojo"])
492            await users.async_for_each(lambda username: signup(username))
493        ```
494
495        Parameters
496        ----------
497        func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]`
498            The async function to call on each item in the iterator.
499        """
500        await _helpers.awaits(*(func(item) for item in self))

Calls the async function on each item in the iterator concurrently.

Example
async def signup(username: str) -> None:
    async with aiohttp.request('POST', '...') as r:
        # Actual logic.
        ...

async def main():
    users = aiobungie.into_iter(["user_danny", "user_jojo"])
    await users.async_for_each(lambda username: signup(username))
Parameters
  • func (collections.Callable[[Item], collections.Coroutine[None, None, None]]): The async function to call on each item in the iterator.
def enumerate( self, *, start: int = 0) -> aiobungie.Iterator[tuple[int, ~Item]]:
502    def enumerate(self, *, start: int = 0) -> Iterator[tuple[int, Item]]:
503        """Returns a new iterator that yields tuples of the index and item.
504
505        Example
506        -------
507        ```py
508        iterator = Iterator([1, 2, 3])
509        for index, item in iterator.enumerate():
510            print(index, item)
511        # 0 1
512        # 1 2
513        # 2 3
514        ```
515
516        Raises
517        ------
518        `StopIteration`
519            If no elements are left in the iterator.
520        """
521        return Iterator(enumerate(self._items, start=start))

Returns a new iterator that yields tuples of the index and item.

Example
iterator = Iterator([1, 2, 3])
for index, item in iterator.enumerate():
    print(index, item)
# 0 1
# 1 2
# 2 3
Raises
  • StopIteration: If no elements are left in the iterator.
@typing.final
class MembershipOption(builtins.int, aiobungie.Enum):
710@typing.final
711class MembershipOption(int, Enum):
712    """A enum for GroupV2 membership options."""
713
714    REVIEWD = 0
715    OPEN = 1
716    CLOSED = 2

A enum for GroupV2 membership options.

REVIEWD = <MembershipOption.REVIEWD: 0>
OPEN = <MembershipOption.OPEN: 1>
CLOSED = <MembershipOption.CLOSED: 2>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class MembershipType(builtins.int, aiobungie.Enum):
458@typing.final
459class MembershipType(int, Enum):
460    """An Enum for Bungie membership types."""
461
462    NONE = 0
463    XBOX = 1
464    PSN = 2
465    STEAM = 3
466    BLIZZARD = 4
467    STADIA = 5
468    EPIC_GAMES_STORE = 6
469    DEMON = 10
470    BUNGIE = 254
471    ALL = -1

An Enum for Bungie membership types.

NONE = <MembershipType.NONE: 0>
XBOX = <MembershipType.XBOX: 1>
PSN = <MembershipType.PSN: 2>
STEAM = <MembershipType.STEAM: 3>
BLIZZARD = <MembershipType.BLIZZARD: 4>
STADIA = <MembershipType.STADIA: 5>
EPIC_GAMES_STORE = <MembershipType.EPIC_GAMES_STORE: 6>
DEMON = <MembershipType.DEMON: 10>
BUNGIE = <MembershipType.BUNGIE: 254>
ALL = <MembershipType.ALL: -1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class MembershipTypeError(aiobungie.BadRequest):
182@attrs.define(auto_exc=True)
183class MembershipTypeError(BadRequest):
184    """A bad request error raised when passing wrong membership to the request.
185
186    Those fields are useful since it returns the correct membership and id which can be used
187    to make the request again with those fields.
188
189    Example
190    -------
191    ```py
192    try:
193        profile = await client.fetch_profile(
194            member_id=1,
195            type=aiobungie.MembershipType.STADIA,
196            components=[]
197        )
198
199    # Membership type is wrong!
200    except aiobungie.MembershipTypeError as err:
201        correct_membersip = err.into_membership()
202        profile_id = err.membership_id
203
204        # Recall the method.
205        profile = await client.fetch_profile(
206            member_id=profile_id,
207            type=correct_membership,
208            components=[]
209        )
210    ```
211    """
212
213    membership_type: str
214    """The errored membership type passed to the request."""
215
216    membership_id: int
217    """The errored user's membership id."""
218
219    required_membership: str
220    """The required correct membership for errored user."""
221
222    def into_membership(
223        self, value: typing.Optional[str] = None
224    ) -> enums.MembershipType:
225        """Turn the required membership from `str` into `aiobungie.Membership` type.
226
227        If value parameter is not provided it will fall back to the required membership.
228        """
229        if value is None:
230            return _DETERMINE_MSHIP(self.required_membership)
231        return _DETERMINE_MSHIP(value)
232
233    def __str__(self) -> str:
234        return (
235            f"Expected membership: {self.into_membership().name.replace('_', '').title()}, "
236            f"But got {self.into_membership(self.membership_type)} for id {self.membership_id}"
237        )
238
239    def __int__(self) -> int:
240        return int(self.membership_id)

A bad request error raised when passing wrong membership to the request.

Those fields are useful since it returns the correct membership and id which can be used to make the request again with those fields.

Example
try:
    profile = await client.fetch_profile(
        member_id=1,
        type=aiobungie.MembershipType.STADIA,
        components=[]
    )

# Membership type is wrong!
except aiobungie.MembershipTypeError as err:
    correct_membersip = err.into_membership()
    profile_id = err.membership_id

    # Recall the method.
    profile = await client.fetch_profile(
        member_id=profile_id,
        type=correct_membership,
        components=[]
    )
MembershipTypeError( message: str, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], membership_type: str, membership_id: int, required_membership: str)
 2def __init__(self, message, url, body, headers, membership_type, membership_id, required_membership):
 3    self.message = message
 4    self.url = url
 5    self.body = body
 6    self.headers = headers
 7    self.http_status = attr_dict['http_status'].default
 8    self.membership_type = membership_type
 9    self.membership_id = membership_id
10    self.required_membership = required_membership
11    BaseException.__init__(self, self.message,self.url,self.body,self.headers,self.membership_type,self.membership_id,self.required_membership)

Method generated by attrs for class MembershipTypeError.

membership_type: str

The errored membership type passed to the request.

membership_id: int

The errored user's membership id.

required_membership: str

The required correct membership for errored user.

def into_membership( self, value: Optional[str] = None) -> aiobungie.MembershipType:
222    def into_membership(
223        self, value: typing.Optional[str] = None
224    ) -> enums.MembershipType:
225        """Turn the required membership from `str` into `aiobungie.Membership` type.
226
227        If value parameter is not provided it will fall back to the required membership.
228        """
229        if value is None:
230            return _DETERMINE_MSHIP(self.required_membership)
231        return _DETERMINE_MSHIP(value)

Turn the required membership from str into aiobungie.Membership type.

If value parameter is not provided it will fall back to the required membership.

Inherited Members
BadRequest
url
body
headers
http_status
HTTPError
message
builtins.BaseException
with_traceback
@typing.final
class MilestoneType(builtins.int, aiobungie.Enum):
503@typing.final
504class MilestoneType(int, Enum):
505    """An Enum for Destiny 2 milestone types."""
506
507    UNKNOWN = 0
508    TUTORIAL = 1
509    ONETIME = 2
510    WEEKLY = 3
511    DAILY = 4
512    SPECIAL = 5

An Enum for Destiny 2 milestone types.

UNKNOWN = <MilestoneType.UNKNOWN: 0>
TUTORIAL = <MilestoneType.TUTORIAL: 1>
ONETIME = <MilestoneType.ONETIME: 2>
WEEKLY = <MilestoneType.WEEKLY: 3>
DAILY = <MilestoneType.DAILY: 4>
SPECIAL = <MilestoneType.SPECIAL: 5>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class NotFound(aiobungie.HTTPException):
146@attrs.define(auto_exc=True)
147class NotFound(HTTPException):
148    """Raised when an unknown resource was not found."""
149
150    http_status: http.HTTPStatus = attrs.field(
151        default=http.HTTPStatus.NOT_FOUND, init=False
152    )

Raised when an unknown resource was not found.

NotFound( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class NotFound.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
@typing.final
class ObjectiveUIStyle(builtins.int, aiobungie.Enum):
 94@typing.final
 95class ObjectiveUIStyle(int, enums.Enum):
 96    NONE = 0
 97    HIGHLIGHTED = 1
 98    CRAFTING_WEAPON_LEVEL = 2
 99    CRAFTING_WEAPON_LEVEL_PROGRESS = 3
100    CRAFTING_WEAPON_TIMESTAMP = 4
101    CRAFTING_MEMENTOS = 5
102    CRAFTING_MEMENTO_TITLE = 6

An enumeration.

NONE = <ObjectiveUIStyle.NONE: 0>
HIGHLIGHTED = <ObjectiveUIStyle.HIGHLIGHTED: 1>
CRAFTING_WEAPON_LEVEL = <ObjectiveUIStyle.CRAFTING_WEAPON_LEVEL: 2>
CRAFTING_WEAPON_LEVEL_PROGRESS = <ObjectiveUIStyle.CRAFTING_WEAPON_LEVEL_PROGRESS: 3>
CRAFTING_WEAPON_TIMESTAMP = <ObjectiveUIStyle.CRAFTING_WEAPON_TIMESTAMP: 4>
CRAFTING_MEMENTOS = <ObjectiveUIStyle.CRAFTING_MEMENTOS: 5>
CRAFTING_MEMENTO_TITLE = <ObjectiveUIStyle.CRAFTING_MEMENTO_TITLE: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Place(builtins.int, aiobungie.Enum):
230@typing.final
231class Place(int, Enum):
232    """An Enum for Destiny 2 Places and NOT Planets"""
233
234    ORBIT = 2961497387
235    SOCIAL = 4151112093
236    LIGHT_HOUSE = 4276116472
237    EXPLORE = 3497767639

An Enum for Destiny 2 Places and NOT Planets

ORBIT = <Place.ORBIT: 2961497387>
SOCIAL = <Place.SOCIAL: 4151112093>
LIGHT_HOUSE = <Place.LIGHT_HOUSE: 4276116472>
EXPLORE = <Place.EXPLORE: 3497767639>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Planet(builtins.int, aiobungie.Enum):
195@typing.final
196class Planet(int, Enum):
197    """An Enum for all available planets in Destiny 2."""
198
199    UNKNOWN = 0
200    """Unknown space"""
201
202    EARTH = 3747705955
203    """Earth"""
204
205    DREAMING_CITY = 2877881518
206    """The Dreaming city."""
207
208    NESSUS = 3526908984
209    """Nessus"""
210
211    MOON = 3325508439
212    """The Moon"""
213
214    COSMODROME = 3990611421
215    """The Cosmodrome"""
216
217    TANGLED_SHORE = 3821439926
218    """The Tangled Shore"""
219
220    VENUS = 3871070152
221    """Venus"""
222
223    EAZ = 541863059  # Exclusive event.
224    """European Aerial Zone"""
225
226    EUROPA = 1729879943
227    """Europa"""

An Enum for all available planets in Destiny 2.

UNKNOWN = <Planet.UNKNOWN: 0>

Unknown space

EARTH = <Planet.EARTH: 3747705955>

Earth

DREAMING_CITY = <Planet.DREAMING_CITY: 2877881518>

The Dreaming city.

NESSUS = <Planet.NESSUS: 3526908984>

Nessus

MOON = <Planet.MOON: 3325508439>

The Moon

COSMODROME = <Planet.COSMODROME: 3990611421>

The Cosmodrome

TANGLED_SHORE = <Planet.TANGLED_SHORE: 3821439926>

The Tangled Shore

VENUS = <Planet.VENUS: 3871070152>

Venus

EAZ = <Planet.EAZ: 541863059>

European Aerial Zone

EUROPA = <Planet.EUROPA: 1729879943>

Europa

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Presence(builtins.int, aiobungie.Enum):
680@typing.final
681class Presence(int, Enum):
682    """An enum for a bungie friend status."""
683
684    OFFLINE_OR_UNKNOWN = 0
685    ONLINE = 1

An enum for a bungie friend status.

OFFLINE_OR_UNKNOWN = <Presence.OFFLINE_OR_UNKNOWN: 0>
ONLINE = <Presence.ONLINE: 1>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class PrivacySetting(builtins.int, aiobungie.Enum):
768@typing.final
769class PrivacySetting(int, Enum):
770    """An enum for players's privacy settings."""
771
772    OPEN = 0
773    CLAN_AND_FRIENDS = 1
774    FRIENDS_ONLY = 2
775    INVITE_ONLY = 3
776    CLOSED = 4

An enum for players's privacy settings.

OPEN = <PrivacySetting.OPEN: 0>
CLAN_AND_FRIENDS = <PrivacySetting.CLAN_AND_FRIENDS: 1>
FRIENDS_ONLY = <PrivacySetting.FRIENDS_ONLY: 2>
INVITE_ONLY = <PrivacySetting.INVITE_ONLY: 3>
CLOSED = <PrivacySetting.CLOSED: 4>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class RESTClient(aiobungie.interfaces.rest.RESTInterface):
 345class RESTClient(interfaces.RESTInterface):
 346    """A RESTful client implementation for Bungie's API.
 347
 348    This client is designed to only make HTTP requests and return JSON objects
 349    to provide RESTful functionality.
 350
 351    This client is also used within `aiobungie.Client` which deserialize those returned JSON objects
 352    using the factory into Pythonic data classes objects which provide Python functionality.
 353
 354    Example
 355    -------
 356    ```py
 357    import aiobungie
 358
 359    async def main():
 360        async with aiobungie.RESTClient("TOKEN") as rest_client:
 361            req = await rest_client.fetch_clan_members(4389205)
 362            clan_members = req['results']
 363            for member in clan_members:
 364                for k, v in member['destinyUserInfo'].items():
 365                    print(k, v)
 366    ```
 367
 368    Parameters
 369    ----------
 370    token : `str`
 371        A valid application token from Bungie's developer portal.
 372
 373    Other Parameters
 374    ----------------
 375    max_retries : `int`
 376        The max retries number to retry if the request hit a `5xx` status code.
 377    client_secret : `typing.Optional[str]`
 378        An optional application client secret,
 379        This is only needed if you're fetching OAuth2 tokens with this client.
 380    client_id : `typing.Optional[int]`
 381        An optional application client id,
 382        This is only needed if you're fetching OAuth2 tokens with this client.
 383    enable_debugging : `bool | str`
 384        Whether to enable logging responses or not.
 385
 386    Logging Levels
 387    --------------
 388    * `False`: This will disable logging.
 389    * `True`: This will set the level to `DEBUG` and enable logging minimal information.
 390    * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
 391    """
 392
 393    __slots__ = (
 394        "_token",
 395        "_session",
 396        "_lock",
 397        "_max_retries",
 398        "_client_secret",
 399        "_client_id",
 400        "_metadata",
 401    )
 402
 403    def __init__(
 404        self,
 405        token: str,
 406        /,
 407        client_secret: typing.Optional[str] = None,
 408        client_id: typing.Optional[int] = None,
 409        *,
 410        max_retries: int = 4,
 411        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 412    ) -> None:
 413        self._session: typing.Optional[_Session] = None
 414        self._lock: typing.Optional[asyncio.Lock] = None
 415        self._client_secret = client_secret
 416        self._client_id = client_id
 417        self._token: str = token
 418        self._max_retries = max_retries
 419        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
 420
 421        self._set_debug_level(enable_debugging)
 422
 423    @property
 424    def client_id(self) -> typing.Optional[int]:
 425        return self._client_id
 426
 427    @property
 428    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
 429        return self._metadata
 430
 431    @property
 432    def is_alive(self) -> bool:
 433        return self._session is not None
 434
 435    @typing.final
 436    async def close(self) -> None:
 437        session = self._get_session()
 438        await session.close()
 439        self._session = None
 440
 441    @typing.final
 442    def open(self) -> None:
 443        """Open a new client session. This is called internally with contextmanager usage."""
 444        if self.is_alive:
 445            raise RuntimeError("Cannot open a new session while it's already open.")
 446
 447        self._session = _Session.create()
 448
 449    @typing.final
 450    def enable_debugging(
 451        self,
 452        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 453        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
 454        /,
 455    ) -> None:
 456        self._set_debug_level(level, file)
 457
 458    @typing.final
 459    async def static_request(
 460        self,
 461        method: typing.Union[RequestMethod, str],
 462        path: str,
 463        *,
 464        auth: typing.Optional[str] = None,
 465        json: typing.Optional[dict[str, typing.Any]] = None,
 466    ) -> ResponseSig:
 467        return await self._request(method, path, auth=auth, json=json)
 468
 469    @typing.final
 470    def build_oauth2_url(
 471        self, client_id: typing.Optional[int] = None
 472    ) -> typing.Optional[builders.OAuthURL]:
 473        client_id = client_id or self._client_id
 474        if client_id is None:
 475            return None
 476
 477        return builders.OAuthURL(client_id=client_id)
 478
 479    @staticmethod
 480    def _set_debug_level(
 481        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
 482        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
 483    ) -> None:
 484
 485        file_handler = logging.FileHandler(file, mode="w") if file else None
 486        if level == "TRACE" or level == TRACE:
 487            logging.basicConfig(
 488                level=TRACE, handlers=[file_handler] if file_handler else None
 489            )
 490
 491        elif level:
 492            logging.basicConfig(
 493                level=logging.DEBUG, handlers=[file_handler] if file_handler else None
 494            )
 495
 496    def _get_session(self) -> _Session:
 497        if self._session:
 498            return self._session
 499
 500        raise RuntimeError(
 501            "Cannot return a session while its close. Make sure you use `async with` before making requests."
 502        )
 503
 504    async def _request(
 505        self,
 506        method: typing.Union[RequestMethod, str],
 507        route: str,
 508        *,
 509        base: bool = False,
 510        oauth2: bool = False,
 511        auth: typing.Optional[str] = None,
 512        unwrapping: typing.Literal["json", "read"] = "json",
 513        json: typing.Optional[dict[str, typing.Any]] = None,
 514        headers: typing.Optional[dict[str, typing.Any]] = None,
 515        data: typing.Optional[typing.Union[str, dict[str, typing.Any]]] = None,
 516    ) -> ResponseSig:
 517
 518        retries: int = 0
 519        session = self._get_session()
 520        headers = headers or {}
 521
 522        headers.setdefault(_USER_AGENT_HEADERS, _USER_AGENT)
 523        headers["X-API-KEY"] = self._token
 524
 525        if auth is not None:
 526            headers[_AUTH_HEADER] = f"Bearer {auth}"
 527
 528        # Handling endpoints
 529        endpoint = url.BASE
 530
 531        if not base:
 532            endpoint = endpoint + url.REST_EP
 533
 534        if oauth2:
 535            headers["Content-Type"] = "application/x-www-form-urlencoded"
 536            endpoint = endpoint + url.TOKEN_EP
 537
 538        if self._lock is None:
 539            self._lock = asyncio.Lock()
 540
 541        while True:
 542            async with (stack := contextlib.AsyncExitStack()):
 543                await stack.enter_async_context(self._lock)
 544
 545                # We make the request here.
 546                taken_time = time.monotonic()
 547                response = await stack.enter_async_context(
 548                    session.client_session.request(
 549                        method=method,
 550                        url=f"{endpoint}/{route}",
 551                        json=json,
 552                        headers=headers,
 553                        data=data,
 554                    )
 555                )
 556                response_time = (time.monotonic() - taken_time) * 1_000
 557
 558                _LOG.debug(
 559                    "%s %s %s Time %.4fms",
 560                    method,
 561                    f"{endpoint}/{route}",
 562                    f"{response.status} {response.reason}",
 563                    response_time,
 564                )
 565
 566                await self._handle_ratelimit(response, method, route)
 567
 568                if response.status == http.HTTPStatus.NO_CONTENT:
 569                    return None
 570
 571                if 300 > response.status >= 200:
 572                    if unwrapping == "read":
 573                        # We need to read the bytes for the manifest response.
 574                        return await response.read()
 575
 576                    if response.content_type == _APP_JSON:
 577                        json_data = await response.json()
 578
 579                        _LOG.debug(
 580                            "%s %s %s Time %.4fms",
 581                            method,
 582                            f"{endpoint}/{route}",
 583                            f"{response.status} {response.reason}",
 584                            response_time,
 585                        )
 586
 587                        if _LOG.isEnabledFor(TRACE):
 588                            cloned = headers.copy()
 589                            cloned.update(response.headers)  # type: ignore
 590
 591                            _LOG.log(
 592                                TRACE,
 593                                "%s",
 594                                error.stringify_http_message(cloned),
 595                            )
 596
 597                        # Return the response.
 598                        # oauth2 responses are not packed inside a Response object.
 599                        if oauth2:
 600                            return json_data  # type: ignore[no-any-return]
 601
 602                        return json_data["Response"]  # type: ignore[no-any-return]
 603
 604                if (
 605                    response.status in _RETRY_5XX
 606                    and retries < self._max_retries  # noqa: W503
 607                ):
 608                    backoff_ = backoff.ExponentialBackOff(maximum=6)
 609                    sleep_time = next(backoff_)
 610                    _LOG.warning(
 611                        "Got %i - %s. Sleeping for %.2f seconds. Remaining retries: %i",
 612                        response.status,
 613                        response.reason,
 614                        sleep_time,
 615                        self._max_retries - retries,
 616                    )
 617
 618                    retries += 1
 619                    await asyncio.sleep(sleep_time)
 620                    continue
 621
 622                raise await error.raise_error(response)
 623
 624    if not typing.TYPE_CHECKING:
 625
 626        def __enter__(self) -> typing.NoReturn:
 627            cls = type(self)
 628            raise TypeError(
 629                f"{cls.__qualname__} is async only, use 'async with' instead."
 630            )
 631
 632        def __exit__(
 633            self,
 634            exception_type: typing.Optional[type[BaseException]],
 635            exception: typing.Optional[BaseException],
 636            exception_traceback: typing.Optional[types.TracebackType],
 637        ) -> None:
 638            ...
 639
 640    async def __aenter__(self) -> RESTClient:
 641        self.open()
 642        return self
 643
 644    async def __aexit__(
 645        self,
 646        exception_type: typing.Optional[type[BaseException]],
 647        exception: typing.Optional[BaseException],
 648        exception_traceback: typing.Optional[types.TracebackType],
 649    ) -> None:
 650        await self.close()
 651
 652    # We don't want this to be super complicated.
 653    @typing.final
 654    async def _handle_ratelimit(
 655        self,
 656        response: aiohttp.ClientResponse,
 657        method: str,
 658        route: str,
 659    ) -> None:
 660
 661        if response.status != http.HTTPStatus.TOO_MANY_REQUESTS:
 662            return
 663
 664        if response.content_type != _APP_JSON:
 665            raise error.HTTPError(
 666                f"Being ratelimited on non JSON request, {response.content_type}.",
 667                http.HTTPStatus.TOO_MANY_REQUESTS,
 668            )
 669
 670        json: typedefs.JSONObject = await response.json()
 671        retry_after = float(json.get("ThrottleSeconds", 15.0)) + 0.1
 672        max_calls: int = 0
 673
 674        while True:
 675            if max_calls == 10:
 676                # Max retries by default. We raise an error here.
 677                raise error.RateLimitedError(
 678                    body=json,
 679                    url=str(response.real_url),
 680                    retry_after=retry_after,
 681                )
 682
 683            # We sleep for a little bit to avoid funky behavior.
 684            _LOG.warning(
 685                "We're being ratelimited, Method %s Route %s. Sleeping for %.2fs.",
 686                method,
 687                route,
 688                retry_after,
 689            )
 690            await asyncio.sleep(retry_after)
 691            max_calls += 1
 692            continue
 693
 694    async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response:
 695        if not isinstance(self._client_secret, (str, int)):
 696            raise TypeError(
 697                "Expected (str, int) for client secret "
 698                f"but got {type(self._client_secret).__name__}"  # type: ignore
 699            )
 700
 701        headers = {
 702            "client_secret": self._client_secret,
 703        }
 704
 705        data = (
 706            f"grant_type=authorization_code&code={code}"
 707            f"&client_id={self._client_id}&client_secret={self._client_secret}"
 708        )
 709
 710        response = await self._request(
 711            RequestMethod.POST, "", headers=headers, data=data, oauth2=True
 712        )
 713        assert isinstance(response, dict)
 714        return builders.OAuth2Response.build_response(response)
 715
 716    async def refresh_access_token(
 717        self, refresh_token: str, /
 718    ) -> builders.OAuth2Response:
 719        if not isinstance(self._client_secret, (int, str)):
 720            raise TypeError(
 721                f"Expected (str, int) for client secret but got {type(self._client_secret).__name__}"  # type: ignore
 722            )
 723
 724        data = {
 725            "grant_type": "refresh_token",
 726            "refresh_token": refresh_token,
 727            "client_id": self._client_id,
 728            "client_secret": self._client_secret,
 729            "Content-Type": "application/x-www-form-urlencoded",
 730        }
 731
 732        response = await self._request(RequestMethod.POST, "", data=data, oauth2=True)
 733        assert isinstance(response, dict)
 734        return builders.OAuth2Response.build_response(response)
 735
 736    async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject:
 737        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 738        resp = await self._request(
 739            RequestMethod.GET, f"User/GetBungieNetUserById/{id}/"
 740        )
 741        assert isinstance(resp, dict)
 742        return resp
 743
 744    async def fetch_user_themes(self) -> typedefs.JSONArray:
 745        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 746        resp = await self._request(RequestMethod.GET, "User/GetAvailableThemes/")
 747        assert isinstance(resp, list)
 748        return resp
 749
 750    async def fetch_membership_from_id(
 751        self,
 752        id: int,
 753        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 754        /,
 755    ) -> typedefs.JSONObject:
 756        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 757        resp = await self._request(
 758            RequestMethod.GET, f"User/GetMembershipsById/{id}/{int(type)}"
 759        )
 760        assert isinstance(resp, dict)
 761        return resp
 762
 763    async def fetch_player(
 764        self,
 765        name: str,
 766        code: int,
 767        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
 768        /,
 769    ) -> typedefs.JSONArray:
 770        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 771        resp = await self._request(
 772            RequestMethod.POST,
 773            f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}",
 774            json={"displayName": name, "displayNameCode": code},
 775        )
 776        assert isinstance(resp, list)
 777        return resp
 778
 779    async def search_users(self, name: str, /) -> typedefs.JSONObject:
 780        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 781        resp = await self._request(
 782            RequestMethod.POST,
 783            "User/Search/GlobalName/0",
 784            json={"displayNamePrefix": name},
 785        )
 786        assert isinstance(resp, dict)
 787        return resp
 788
 789    async def fetch_clan_from_id(
 790        self, id: int, /, access_token: typing.Optional[str] = None
 791    ) -> typedefs.JSONObject:
 792        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 793        resp = await self._request(
 794            RequestMethod.GET, f"GroupV2/{id}", auth=access_token
 795        )
 796        assert isinstance(resp, dict)
 797        return resp
 798
 799    async def fetch_clan(
 800        self,
 801        name: str,
 802        /,
 803        access_token: typing.Optional[str] = None,
 804        *,
 805        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 806    ) -> typedefs.JSONObject:
 807        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 808        resp = await self._request(
 809            RequestMethod.GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token
 810        )
 811        assert isinstance(resp, dict)
 812        return resp
 813
 814    async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject:
 815        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 816        resp = await self._request(
 817            RequestMethod.GET, f"GroupV2/{clan_id}/AdminsAndFounder/"
 818        )
 819        assert isinstance(resp, dict)
 820        return resp
 821
 822    async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray:
 823        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 824        resp = await self._request(
 825            RequestMethod.GET, f"GroupV2/{clan_id}/OptionalConversations/"
 826        )
 827        assert isinstance(resp, list)
 828        return resp
 829
 830    async def fetch_application(self, appid: int, /) -> typedefs.JSONObject:
 831        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 832        resp = await self._request(RequestMethod.GET, f"App/Application/{appid}")
 833        assert isinstance(resp, dict)
 834        return resp
 835
 836    async def fetch_character(
 837        self,
 838        member_id: int,
 839        membership_type: typedefs.IntAnd[enums.MembershipType],
 840        character_id: int,
 841        components: list[enums.ComponentType],
 842        auth: typing.Optional[str] = None,
 843    ) -> typedefs.JSONObject:
 844        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 845        collector = _collect_components(components)
 846        response = await self._request(
 847            RequestMethod.GET,
 848            f"Destiny2/{int(membership_type)}/Profile/{member_id}/"
 849            f"Character/{character_id}/?components={collector}",
 850            auth=auth,
 851        )
 852        assert isinstance(response, dict)
 853        return response
 854
 855    async def fetch_activities(
 856        self,
 857        member_id: int,
 858        character_id: int,
 859        mode: typedefs.IntAnd[enums.GameMode],
 860        membership_type: typedefs.IntAnd[
 861            enums.MembershipType
 862        ] = enums.MembershipType.ALL,
 863        *,
 864        page: int = 0,
 865        limit: int = 1,
 866    ) -> typedefs.JSONObject:
 867        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 868        resp = await self._request(
 869            RequestMethod.GET,
 870            f"Destiny2/{int(membership_type)}/Account/"
 871            f"{member_id}/Character/{character_id}/Stats/Activities"
 872            f"/?mode={int(mode)}&count={limit}&page={page}",
 873        )
 874        assert isinstance(resp, dict)
 875        return resp
 876
 877    async def fetch_vendor_sales(self) -> typedefs.JSONObject:
 878        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 879        resp = await self._request(
 880            RequestMethod.GET,
 881            f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}",
 882        )
 883        assert isinstance(resp, dict)
 884        return resp
 885
 886    async def fetch_profile(
 887        self,
 888        membership_id: int,
 889        type: typedefs.IntAnd[enums.MembershipType],
 890        components: list[enums.ComponentType],
 891        auth: typing.Optional[str] = None,
 892    ) -> typedefs.JSONObject:
 893        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 894        collector = _collect_components(components)
 895        response = await self._request(
 896            RequestMethod.GET,
 897            f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}",
 898            auth=auth,
 899        )
 900        assert isinstance(response, dict)
 901        return response
 902
 903    async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject:
 904        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 905        response = await self._request(
 906            RequestMethod.GET, route=f"Destiny2/Manifest/{type}/{hash}"
 907        )
 908        assert isinstance(response, dict)
 909        return response
 910
 911    async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject:
 912        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 913        resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash)
 914        assert isinstance(resp, dict)
 915        return resp
 916
 917    async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject:
 918        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 919        resp = await self.fetch_entity("DestinyObjectiveDefinition", hash)
 920        assert isinstance(resp, dict)
 921        return resp
 922
 923    async def fetch_groups_for_member(
 924        self,
 925        member_id: int,
 926        member_type: typedefs.IntAnd[enums.MembershipType],
 927        /,
 928        *,
 929        filter: int = 0,
 930        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 931    ) -> typedefs.JSONObject:
 932        resp = await self._request(
 933            RequestMethod.GET,
 934            f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
 935        )
 936        assert isinstance(resp, dict)
 937        return resp
 938
 939    async def fetch_potential_groups_for_member(
 940        self,
 941        member_id: int,
 942        member_type: typedefs.IntAnd[enums.MembershipType],
 943        /,
 944        *,
 945        filter: int = 0,
 946        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
 947    ) -> typedefs.JSONObject:
 948        resp = await self._request(
 949            RequestMethod.GET,
 950            f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
 951        )
 952        assert isinstance(resp, dict)
 953        return resp
 954
 955    async def fetch_clan_members(
 956        self,
 957        clan_id: int,
 958        /,
 959        *,
 960        name: typing.Optional[str] = None,
 961        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
 962    ) -> typedefs.JSONObject:
 963        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 964        resp = await self._request(
 965            RequestMethod.GET,
 966            f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}&currentpage=1",
 967        )
 968        assert isinstance(resp, dict)
 969        return resp
 970
 971    async def fetch_hardlinked_credentials(
 972        self,
 973        credential: int,
 974        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
 975        /,
 976    ) -> typedefs.JSONObject:
 977        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
 978        resp = await self._request(
 979            RequestMethod.GET,
 980            f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/",
 981        )
 982        assert isinstance(resp, dict)
 983        return resp
 984
 985    async def fetch_user_credentials(
 986        self, access_token: str, membership_id: int, /
 987    ) -> typedefs.JSONArray:
 988        resp = await self._request(
 989            RequestMethod.GET,
 990            f"User/GetCredentialTypesForTargetAccount/{membership_id}",
 991            auth=access_token,
 992        )
 993        assert isinstance(resp, list)
 994        return resp
 995
 996    async def insert_socket_plug(
 997        self,
 998        action_token: str,
 999        /,
1000        instance_id: int,
1001        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1002        character_id: int,
1003        membership_type: typedefs.IntAnd[enums.MembershipType],
1004    ) -> typedefs.JSONObject:
1005
1006        if isinstance(plug, builders.PlugSocketBuilder):
1007            plug = plug.collect()
1008
1009        body = {
1010            "actionToken": action_token,
1011            "itemInstanceId": instance_id,
1012            "plug": plug,
1013            "characterId": character_id,
1014            "membershipType": int(membership_type),
1015        }
1016        resp = await self._request(
1017            RequestMethod.POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body
1018        )
1019        assert isinstance(resp, dict)
1020        return resp
1021
1022    async def insert_socket_plug_free(
1023        self,
1024        access_token: str,
1025        /,
1026        instance_id: int,
1027        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1028        character_id: int,
1029        membership_type: typedefs.IntAnd[enums.MembershipType],
1030    ) -> typedefs.JSONObject:
1031
1032        if isinstance(plug, builders.PlugSocketBuilder):
1033            plug = plug.collect()
1034
1035        body = {
1036            "itemInstanceId": instance_id,
1037            "plug": plug,
1038            "characterId": character_id,
1039            "membershipType": int(membership_type),
1040        }
1041        resp = await self._request(
1042            RequestMethod.POST,
1043            "Destiny2/Actions/Items/InsertSocketPlugFree",
1044            json=body,
1045            auth=access_token,
1046        )
1047        assert isinstance(resp, dict)
1048        return resp
1049
1050    async def set_item_lock_state(
1051        self,
1052        access_token: str,
1053        state: bool,
1054        /,
1055        item_id: int,
1056        character_id: int,
1057        membership_type: typedefs.IntAnd[enums.MembershipType],
1058    ) -> int:
1059        body = {
1060            "state": state,
1061            "itemId": item_id,
1062            "characterId": character_id,
1063            "membership_type": int(membership_type),
1064        }
1065        response = await self._request(
1066            RequestMethod.POST,
1067            "Destiny2/Actions/Items/SetLockState",
1068            json=body,
1069            auth=access_token,
1070        )
1071        assert isinstance(response, int)
1072        return response
1073
1074    async def set_quest_track_state(
1075        self,
1076        access_token: str,
1077        state: bool,
1078        /,
1079        item_id: int,
1080        character_id: int,
1081        membership_type: typedefs.IntAnd[enums.MembershipType],
1082    ) -> int:
1083        body = {
1084            "state": state,
1085            "itemId": item_id,
1086            "characterId": character_id,
1087            "membership_type": int(membership_type),
1088        }
1089        response = await self._request(
1090            RequestMethod.POST,
1091            "Destiny2/Actions/Items/SetTrackedState",
1092            json=body,
1093            auth=access_token,
1094        )
1095        assert isinstance(response, int)
1096        return response
1097
1098    async def fetch_manifest_path(self) -> typedefs.JSONObject:
1099        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1100        path = await self._request(RequestMethod.GET, "Destiny2/Manifest")
1101        assert isinstance(path, dict)
1102        return path
1103
1104    async def read_manifest_bytes(self, language: str = "en", /) -> bytes:
1105        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1106        _ensure_manifest_language(language)
1107
1108        content = await self.fetch_manifest_path()
1109        resp = await self._request(
1110            RequestMethod.GET,
1111            content["mobileWorldContentPaths"][language],
1112            unwrapping="read",
1113            base=True,
1114        )
1115        assert isinstance(resp, bytes)
1116        return resp
1117
1118    async def download_manifest(
1119        self,
1120        language: str = "en",
1121        name: str = "manifest",
1122        path: typing.Union[pathlib.Path, str] = ".",
1123        *,
1124        force: bool = False,
1125    ) -> None:
1126        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1127        complete_path = _get_path(name, path, sql=True)
1128
1129        if complete_path.exists() and force:
1130            if force:
1131                _LOG.info(
1132                    f"Found manifest in {complete_path!s}. Forcing to Re-Download."
1133                )
1134                complete_path.unlink(missing_ok=True)
1135
1136                return await self.download_manifest(language, name, path, force=force)
1137
1138            else:
1139                raise FileExistsError(
1140                    "Manifest file already exists, "
1141                    "To force download, set the `force` parameter to `True`."
1142                )
1143
1144        _LOG.info(f"Downloading manifest. Location: {complete_path!s}")
1145        data_bytes = await self.read_manifest_bytes(language)
1146        await asyncio.get_running_loop().run_in_executor(
1147            None, _write_sqlite_bytes, data_bytes, path, name
1148        )
1149
1150    async def download_json_manifest(
1151        self,
1152        file_name: str = "manifest",
1153        path: typing.Union[str, pathlib.Path] = ".",
1154        language: str = "en",
1155    ) -> None:
1156        _ensure_manifest_language(language)
1157
1158        _LOG.info(f"Downloading manifest JSON to {_get_path(file_name, path)!r}...")
1159
1160        content = await self.fetch_manifest_path()
1161        json_bytes = await self._request(
1162            RequestMethod.GET,
1163            content["jsonWorldContentPaths"][language],
1164            unwrapping="read",
1165            base=True,
1166        )
1167
1168        await asyncio.get_running_loop().run_in_executor(
1169            None, _write_json_bytes, json_bytes, file_name, path
1170        )
1171        _LOG.info("Finished downloading manifest JSON.")
1172
1173    async def fetch_manifest_version(self) -> str:
1174        return typing.cast(str, (await self.fetch_manifest_path())["version"])
1175
1176    async def fetch_linked_profiles(
1177        self,
1178        member_id: int,
1179        member_type: typedefs.IntAnd[enums.MembershipType],
1180        /,
1181        *,
1182        all: bool = False,
1183    ) -> typedefs.JSONObject:
1184        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1185        resp = await self._request(
1186            RequestMethod.GET,
1187            f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}",
1188        )
1189        assert isinstance(resp, dict)
1190        return resp
1191
1192    async def fetch_clan_banners(self) -> typedefs.JSONObject:
1193        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1194        resp = await self._request(
1195            RequestMethod.GET, "Destiny2/Clan/ClanBannerDictionary/"
1196        )
1197        assert isinstance(resp, dict)
1198        return resp
1199
1200    async def fetch_public_milestones(self) -> typedefs.JSONObject:
1201        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1202        resp = await self._request(RequestMethod.GET, "Destiny2/Milestones/")
1203        assert isinstance(resp, dict)
1204        return resp
1205
1206    async def fetch_public_milestone_content(
1207        self, milestone_hash: int, /
1208    ) -> typedefs.JSONObject:
1209        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1210        resp = await self._request(
1211            RequestMethod.GET, f"Destiny2/Milestones/{milestone_hash}/Content/"
1212        )
1213        assert isinstance(resp, dict)
1214        return resp
1215
1216    async def fetch_current_user_memberships(
1217        self, access_token: str, /
1218    ) -> typedefs.JSONObject:
1219        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1220        resp = await self._request(
1221            RequestMethod.GET,
1222            "User/GetMembershipsForCurrentUser/",
1223            auth=access_token,
1224        )
1225        assert isinstance(resp, dict)
1226        return resp
1227
1228    async def equip_item(
1229        self,
1230        access_token: str,
1231        /,
1232        item_id: int,
1233        character_id: int,
1234        membership_type: typedefs.IntAnd[enums.MembershipType],
1235    ) -> None:
1236        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1237        payload = {
1238            "itemId": item_id,
1239            "characterId": character_id,
1240            "membershipType": int(membership_type),
1241        }
1242
1243        await self._request(
1244            RequestMethod.POST,
1245            "Destiny2/Actions/Items/EquipItem/",
1246            json=payload,
1247            auth=access_token,
1248        )
1249
1250    async def equip_items(
1251        self,
1252        access_token: str,
1253        /,
1254        item_ids: list[int],
1255        character_id: int,
1256        membership_type: typedefs.IntAnd[enums.MembershipType],
1257    ) -> None:
1258        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1259        payload = {
1260            "itemIds": item_ids,
1261            "characterId": character_id,
1262            "membershipType": int(membership_type),
1263        }
1264        await self._request(
1265            RequestMethod.POST,
1266            "Destiny2/Actions/Items/EquipItems/",
1267            json=payload,
1268            auth=access_token,
1269        )
1270
1271    async def ban_clan_member(
1272        self,
1273        access_token: str,
1274        /,
1275        group_id: int,
1276        membership_id: int,
1277        membership_type: typedefs.IntAnd[enums.MembershipType],
1278        *,
1279        length: int = 0,
1280        comment: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1281    ) -> None:
1282        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1283        payload = {"comment": str(comment), "length": length}
1284        await self._request(
1285            RequestMethod.POST,
1286            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/",
1287            json=payload,
1288            auth=access_token,
1289        )
1290
1291    async def unban_clan_member(
1292        self,
1293        access_token: str,
1294        /,
1295        group_id: int,
1296        membership_id: int,
1297        membership_type: typedefs.IntAnd[enums.MembershipType],
1298    ) -> None:
1299        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1300        await self._request(
1301            RequestMethod.POST,
1302            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/",
1303            auth=access_token,
1304        )
1305
1306    async def kick_clan_member(
1307        self,
1308        access_token: str,
1309        /,
1310        group_id: int,
1311        membership_id: int,
1312        membership_type: typedefs.IntAnd[enums.MembershipType],
1313    ) -> typedefs.JSONObject:
1314        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1315        resp = await self._request(
1316            RequestMethod.POST,
1317            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/",
1318            auth=access_token,
1319        )
1320        assert isinstance(resp, dict)
1321        return resp
1322
1323    async def edit_clan(
1324        self,
1325        access_token: str,
1326        /,
1327        group_id: int,
1328        *,
1329        name: typedefs.NoneOr[str] = None,
1330        about: typedefs.NoneOr[str] = None,
1331        motto: typedefs.NoneOr[str] = None,
1332        theme: typedefs.NoneOr[str] = None,
1333        tags: typedefs.NoneOr[collections.Sequence[str]] = None,
1334        is_public: typedefs.NoneOr[bool] = None,
1335        locale: typedefs.NoneOr[str] = None,
1336        avatar_image_index: typedefs.NoneOr[int] = None,
1337        membership_option: typedefs.NoneOr[
1338            typedefs.IntAnd[enums.MembershipOption]
1339        ] = None,
1340        allow_chat: typedefs.NoneOr[bool] = None,
1341        chat_security: typedefs.NoneOr[typing.Literal[0, 1]] = None,
1342        call_sign: typedefs.NoneOr[str] = None,
1343        homepage: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1344        enable_invite_messaging_for_admins: typedefs.NoneOr[bool] = None,
1345        default_publicity: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1346        is_public_topic_admin: typedefs.NoneOr[bool] = None,
1347    ) -> None:
1348        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1349        payload = {
1350            "name": name,
1351            "about": about,
1352            "motto": motto,
1353            "theme": theme,
1354            "tags": tags,
1355            "isPublic": is_public,
1356            "avatarImageIndex": avatar_image_index,
1357            "isPublicTopicAdminOnly": is_public_topic_admin,
1358            "allowChat": allow_chat,
1359            "chatSecurity": chat_security,
1360            "callsign": call_sign,
1361            "homepage": homepage,
1362            "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins,
1363            "defaultPublicity": default_publicity,
1364            "locale": locale,
1365        }
1366        if membership_option is not None:
1367            payload["membershipOption"] = int(membership_option)
1368
1369        await self._request(
1370            RequestMethod.POST,
1371            f"GroupV2/{group_id}/Edit",
1372            json=payload,
1373            auth=access_token,
1374        )
1375
1376    async def edit_clan_options(
1377        self,
1378        access_token: str,
1379        /,
1380        group_id: int,
1381        *,
1382        invite_permissions_override: typedefs.NoneOr[bool] = None,
1383        update_culture_permissionOverride: typedefs.NoneOr[bool] = None,
1384        host_guided_game_permission_override: typedefs.NoneOr[
1385            typing.Literal[0, 1, 2]
1386        ] = None,
1387        update_banner_permission_override: typedefs.NoneOr[bool] = None,
1388        join_level: typedefs.NoneOr[typedefs.IntAnd[enums.ClanMemberType]] = None,
1389    ) -> None:
1390
1391        payload = {
1392            "InvitePermissionOverride": invite_permissions_override,
1393            "UpdateCulturePermissionOverride": update_culture_permissionOverride,
1394            "HostGuidedGamePermissionOverride": host_guided_game_permission_override,
1395            "UpdateBannerPermissionOverride": update_banner_permission_override,
1396            "JoinLevel": int(join_level) if join_level else None,
1397        }
1398
1399        await self._request(
1400            RequestMethod.POST,
1401            f"GroupV2/{group_id}/EditFounderOptions",
1402            json=payload,
1403            auth=access_token,
1404        )
1405
1406    async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject:
1407        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1408        resp = await self._request(
1409            RequestMethod.GET,
1410            "Social/Friends/",
1411            auth=access_token,
1412        )
1413        assert isinstance(resp, dict)
1414        return resp
1415
1416    async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject:
1417        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1418        resp = await self._request(
1419            RequestMethod.GET,
1420            "Social/Friends/Requests",
1421            auth=access_token,
1422        )
1423        assert isinstance(resp, dict)
1424        return resp
1425
1426    async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1427        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1428        await self._request(
1429            RequestMethod.POST,
1430            f"Social/Friends/Requests/Accept/{member_id}",
1431            auth=access_token,
1432        )
1433
1434    async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1435        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1436        await self._request(
1437            RequestMethod.POST,
1438            f"Social/Friends/Add/{member_id}",
1439            auth=access_token,
1440        )
1441
1442    async def decline_friend_request(
1443        self, access_token: str, /, member_id: int
1444    ) -> None:
1445        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1446        await self._request(
1447            RequestMethod.POST,
1448            f"Social/Friends/Requests/Decline/{member_id}",
1449            auth=access_token,
1450        )
1451
1452    async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1453        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1454        await self._request(
1455            RequestMethod.POST,
1456            f"Social/Friends/Remove/{member_id}",
1457            auth=access_token,
1458        )
1459
1460    async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1461        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1462        await self._request(
1463            RequestMethod.POST,
1464            f"Social/Friends/Requests/Remove/{member_id}",
1465            auth=access_token,
1466        )
1467
1468    async def approve_all_pending_group_users(
1469        self,
1470        access_token: str,
1471        /,
1472        group_id: int,
1473        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1474    ) -> None:
1475        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1476        await self._request(
1477            RequestMethod.POST,
1478            f"GroupV2/{group_id}/Members/ApproveAll",
1479            auth=access_token,
1480            json={"message": str(message)},
1481        )
1482
1483    async def deny_all_pending_group_users(
1484        self,
1485        access_token: str,
1486        /,
1487        group_id: int,
1488        *,
1489        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1490    ) -> None:
1491        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1492        await self._request(
1493            RequestMethod.POST,
1494            f"GroupV2/{group_id}/Members/DenyAll",
1495            auth=access_token,
1496            json={"message": str(message)},
1497        )
1498
1499    async def add_optional_conversation(
1500        self,
1501        access_token: str,
1502        /,
1503        group_id: int,
1504        *,
1505        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1506        security: typing.Literal[0, 1] = 0,
1507    ) -> None:
1508        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1509        payload = {"chatName": str(name), "chatSecurity": security}
1510        await self._request(
1511            RequestMethod.POST,
1512            f"GroupV2/{group_id}/OptionalConversations/Add",
1513            json=payload,
1514            auth=access_token,
1515        )
1516
1517    async def edit_optional_conversation(
1518        self,
1519        access_token: str,
1520        /,
1521        group_id: int,
1522        conversation_id: int,
1523        *,
1524        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1525        security: typing.Literal[0, 1] = 0,
1526        enable_chat: bool = False,
1527    ) -> None:
1528        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1529        payload = {
1530            "chatEnabled": enable_chat,
1531            "chatName": str(name),
1532            "chatSecurity": security,
1533        }
1534        await self._request(
1535            RequestMethod.POST,
1536            f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}",
1537            json=payload,
1538            auth=access_token,
1539        )
1540
1541    async def transfer_item(
1542        self,
1543        access_token: str,
1544        /,
1545        item_id: int,
1546        item_hash: int,
1547        character_id: int,
1548        member_type: typedefs.IntAnd[enums.MembershipType],
1549        *,
1550        stack_size: int = 1,
1551        vault: bool = False,
1552    ) -> None:
1553        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1554        payload = {
1555            "characterId": character_id,
1556            "membershipType": int(member_type),
1557            "itemId": item_id,
1558            "itemReferenceHash": item_hash,
1559            "stackSize": stack_size,
1560            "transferToVault": vault,
1561        }
1562        await self._request(
1563            RequestMethod.POST,
1564            "Destiny2/Actions/Items/TransferItem",
1565            json=payload,
1566            auth=access_token,
1567        )
1568
1569    async def pull_item(
1570        self,
1571        access_token: str,
1572        /,
1573        item_id: int,
1574        item_hash: int,
1575        character_id: int,
1576        member_type: typedefs.IntAnd[enums.MembershipType],
1577        *,
1578        stack_size: int = 1,
1579        vault: bool = False,
1580    ) -> None:
1581        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1582        payload = {
1583            "characterId": character_id,
1584            "membershipType": int(member_type),
1585            "itemId": item_id,
1586            "itemReferenceHash": item_hash,
1587            "stackSize": stack_size,
1588            "transferToVault": vault,
1589        }
1590        await self._request(
1591            RequestMethod.POST,
1592            "Destiny2/Actions/Items/PullFromPostmaster",
1593            json=payload,
1594            auth=access_token,
1595        )
1596
1597    async def fetch_fireteams(
1598        self,
1599        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1600        *,
1601        platform: typedefs.IntAnd[
1602            fireteams.FireteamPlatform
1603        ] = fireteams.FireteamPlatform.ANY,
1604        language: typing.Union[
1605            fireteams.FireteamLanguage, str
1606        ] = fireteams.FireteamLanguage.ALL,
1607        date_range: typedefs.IntAnd[
1608            fireteams.FireteamDate
1609        ] = fireteams.FireteamDate.ALL,
1610        page: int = 0,
1611        slots_filter: int = 0,
1612    ) -> typedefs.JSONObject:
1613        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1614        resp = await self._request(
1615            RequestMethod.GET,
1616            f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}",  # noqa: E501 Line too long
1617        )
1618        assert isinstance(resp, dict)
1619        return resp
1620
1621    async def fetch_avaliable_clan_fireteams(
1622        self,
1623        access_token: str,
1624        group_id: int,
1625        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1626        *,
1627        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1628        language: typing.Union[fireteams.FireteamLanguage, str],
1629        date_range: typedefs.IntAnd[
1630            fireteams.FireteamDate
1631        ] = fireteams.FireteamDate.ALL,
1632        page: int = 0,
1633        public_only: bool = False,
1634        slots_filter: int = 0,
1635    ) -> typedefs.JSONObject:
1636        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1637        resp = await self._request(
1638            RequestMethod.GET,
1639            f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}",  # noqa: E501
1640            json={"langFilter": str(language)},
1641            auth=access_token,
1642        )
1643        assert isinstance(resp, dict)
1644        return resp
1645
1646    async def fetch_clan_fireteam(
1647        self, access_token: str, fireteam_id: int, group_id: int
1648    ) -> typedefs.JSONObject:
1649        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1650        resp = await self._request(
1651            RequestMethod.GET,
1652            f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}",
1653            auth=access_token,
1654        )
1655        assert isinstance(resp, dict)
1656        return resp
1657
1658    async def fetch_my_clan_fireteams(
1659        self,
1660        access_token: str,
1661        group_id: int,
1662        *,
1663        include_closed: bool = True,
1664        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1665        language: typing.Union[fireteams.FireteamLanguage, str],
1666        filtered: bool = True,
1667        page: int = 0,
1668    ) -> typedefs.JSONObject:
1669        payload = {"groupFilter": filtered, "langFilter": str(language)}
1670        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1671        resp = await self._request(
1672            RequestMethod.GET,
1673            f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}",
1674            json=payload,
1675            auth=access_token,
1676        )
1677        assert isinstance(resp, dict)
1678        return resp
1679
1680    async def fetch_private_clan_fireteams(
1681        self, access_token: str, group_id: int, /
1682    ) -> int:
1683        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1684        resp = await self._request(
1685            RequestMethod.GET,
1686            f"Fireteam/Clan/{group_id}/ActiveCount",
1687            auth=access_token,
1688        )
1689        assert isinstance(resp, int)
1690        return resp
1691
1692    async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject:
1693        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1694        resp = await self._request(
1695            RequestMethod.GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}"
1696        )
1697        assert isinstance(resp, dict)
1698        return resp
1699
1700    async def search_entities(
1701        self, name: str, entity_type: str, *, page: int = 0
1702    ) -> typedefs.JSONObject:
1703        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1704        resp = await self._request(
1705            RequestMethod.GET,
1706            f"Destiny2/Armory/Search/{entity_type}/{name}/",
1707            json={"page": page},
1708        )
1709        assert isinstance(resp, dict)
1710        return resp
1711
1712    async def fetch_unique_weapon_history(
1713        self,
1714        membership_id: int,
1715        character_id: int,
1716        membership_type: typedefs.IntAnd[enums.MembershipType],
1717    ) -> typedefs.JSONObject:
1718        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1719        resp = await self._request(
1720            RequestMethod.GET,
1721            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/",
1722        )
1723        assert isinstance(resp, dict)
1724        return resp
1725
1726    async def fetch_item(
1727        self,
1728        member_id: int,
1729        item_id: int,
1730        membership_type: typedefs.IntAnd[enums.MembershipType],
1731        components: list[enums.ComponentType],
1732    ) -> typedefs.JSONObject:
1733        collector = _collect_components(components)
1734        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1735        resp = await self._request(
1736            RequestMethod.GET,
1737            f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}",
1738        )
1739        assert isinstance(resp, dict)
1740        return resp
1741
1742    async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject:
1743        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1744        resp = await self._request(
1745            RequestMethod.GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/"
1746        )
1747        assert isinstance(resp, dict)
1748        return resp
1749
1750    async def fetch_available_locales(self) -> typedefs.JSONObject:
1751        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1752        resp = await self._request(
1753            RequestMethod.GET, "Destiny2/Manifest/DestinyLocaleDefinition/"
1754        )
1755        assert isinstance(resp, dict)
1756        return resp
1757
1758    async def fetch_common_settings(self) -> typedefs.JSONObject:
1759        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1760        resp = await self._request(RequestMethod.GET, "Settings")
1761        assert isinstance(resp, dict)
1762        return resp
1763
1764    async def fetch_user_systems_overrides(self) -> typedefs.JSONObject:
1765        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1766        resp = await self._request(RequestMethod.GET, "UserSystemOverrides")
1767        assert isinstance(resp, dict)
1768        return resp
1769
1770    async def fetch_global_alerts(
1771        self, *, include_streaming: bool = False
1772    ) -> typedefs.JSONArray:
1773        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1774        resp = await self._request(
1775            RequestMethod.GET, f"GlobalAlerts/?includestreaming={include_streaming}"
1776        )
1777        assert isinstance(resp, list)
1778        return resp
1779
1780    async def awainitialize_request(
1781        self,
1782        access_token: str,
1783        type: typing.Literal[0, 1],
1784        membership_type: typedefs.IntAnd[enums.MembershipType],
1785        /,
1786        *,
1787        affected_item_id: typing.Optional[int] = None,
1788        character_id: typing.Optional[int] = None,
1789    ) -> typedefs.JSONObject:
1790        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1791
1792        body = {"type": type, "membershipType": int(membership_type)}
1793
1794        if affected_item_id is not None:
1795            body["affectedItemId"] = affected_item_id
1796
1797        if character_id is not None:
1798            body["characterId"] = character_id
1799
1800        resp = await self._request(
1801            RequestMethod.POST, "Destiny2/Awa/Initialize", json=body, auth=access_token
1802        )
1803        assert isinstance(resp, dict)
1804        return resp
1805
1806    async def awaget_action_token(
1807        self, access_token: str, correlation_id: str, /
1808    ) -> typedefs.JSONObject:
1809        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1810        resp = await self._request(
1811            RequestMethod.POST,
1812            f"Destiny2/Awa/GetActionToken/{correlation_id}",
1813            auth=access_token,
1814        )
1815        assert isinstance(resp, dict)
1816        return resp
1817
1818    async def awa_provide_authorization_result(
1819        self,
1820        access_token: str,
1821        selection: int,
1822        correlation_id: str,
1823        nonce: collections.MutableSequence[typing.Union[str, bytes]],
1824    ) -> int:
1825        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1826
1827        body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce}
1828
1829        resp = await self._request(
1830            RequestMethod.POST,
1831            "Destiny2/Awa/AwaProvideAuthorizationResult",
1832            json=body,
1833            auth=access_token,
1834        )
1835        assert isinstance(resp, int)
1836        return resp
1837
1838    async def fetch_vendors(
1839        self,
1840        access_token: str,
1841        character_id: int,
1842        membership_id: int,
1843        membership_type: typedefs.IntAnd[enums.MembershipType],
1844        /,
1845        components: list[enums.ComponentType],
1846        filter: typing.Optional[int] = None,
1847    ) -> typedefs.JSONObject:
1848        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1849        components_ = _collect_components(components)
1850        route = (
1851            f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1852            f"/Character/{character_id}/Vendors/?components={components_}"
1853        )
1854
1855        if filter is not None:
1856            route = route + f"&filter={filter}"
1857
1858        resp = await self._request(
1859            RequestMethod.GET,
1860            route,
1861            auth=access_token,
1862        )
1863        assert isinstance(resp, dict)
1864        return resp
1865
1866    async def fetch_vendor(
1867        self,
1868        access_token: str,
1869        character_id: int,
1870        membership_id: int,
1871        membership_type: typedefs.IntAnd[enums.MembershipType],
1872        vendor_hash: int,
1873        /,
1874        components: list[enums.ComponentType],
1875    ) -> typedefs.JSONObject:
1876        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1877        components_ = _collect_components(components)
1878        resp = await self._request(
1879            RequestMethod.GET,
1880            (
1881                f"Platform/Destiny2/{int(membership_type)}/Profile/{membership_id}"
1882                f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}"
1883            ),
1884            auth=access_token,
1885        )
1886        assert isinstance(resp, dict)
1887        return resp
1888
1889    async def fetch_application_api_usage(
1890        self,
1891        access_token: str,
1892        application_id: int,
1893        /,
1894        *,
1895        start: typing.Optional[datetime.datetime] = None,
1896        end: typing.Optional[datetime.datetime] = None,
1897    ) -> typedefs.JSONObject:
1898
1899        end_date, start_date = time.parse_date_range(end, start)
1900        resp = await self._request(
1901            RequestMethod.GET,
1902            f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}",
1903            auth=access_token,
1904        )
1905        assert isinstance(resp, dict)
1906        return resp
1907
1908    async def fetch_bungie_applications(self) -> typedefs.JSONArray:
1909        resp = await self._request(RequestMethod.GET, "App/FirstParty")
1910        assert isinstance(resp, list)
1911        return resp
1912
1913    async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject:
1914        resp = await self._request(RequestMethod.GET, f"Content/GetContentType/{type}/")
1915        assert isinstance(resp, dict)
1916        return resp
1917
1918    async def fetch_content_by_id(
1919        self, id: int, locale: str, /, *, head: bool = False
1920    ) -> typedefs.JSONObject:
1921        resp = await self._request(
1922            RequestMethod.GET,
1923            f"Content/GetContentById/{id}/{locale}/",
1924            json={"head": head},
1925        )
1926        assert isinstance(resp, dict)
1927        return resp
1928
1929    async def fetch_content_by_tag_and_type(
1930        self, locale: str, tag: str, type: str, *, head: bool = False
1931    ) -> typedefs.JSONObject:
1932        resp = await self._request(
1933            RequestMethod.GET,
1934            f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/",
1935            json={"head": head},
1936        )
1937        assert isinstance(resp, dict)
1938        return resp
1939
1940    async def search_content_with_text(
1941        self,
1942        locale: str,
1943        /,
1944        content_type: str,
1945        search_text: str,
1946        tag: str,
1947        *,
1948        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1949        source: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1950    ) -> typedefs.JSONObject:
1951
1952        body: typedefs.JSONObject = {}
1953
1954        body["ctype"] = content_type
1955        body["searchtext"] = search_text
1956        body["tag"] = tag
1957
1958        if page is not undefined.UNDEFINED:
1959            body["currentpage"] = page
1960        else:
1961            body["currentpage"] = 1
1962
1963        if source is not undefined.UNDEFINED:
1964            body["source"] = source
1965        else:
1966            source = ""
1967        resp = await self._request(
1968            RequestMethod.GET, f"Content/Search/{locale}/", json=body
1969        )
1970        assert isinstance(resp, dict)
1971        return resp
1972
1973    async def search_content_by_tag_and_type(
1974        self,
1975        locale: str,
1976        tag: str,
1977        type: str,
1978        *,
1979        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1980    ) -> typedefs.JSONObject:
1981        body: typedefs.JSONObject = {}
1982        body["currentpage"] = 1 if page is undefined.UNDEFINED else page
1983        resp = await self._request(
1984            RequestMethod.GET,
1985            f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/",
1986            json=body,
1987        )
1988        assert isinstance(resp, dict)
1989        return resp
1990
1991    async def search_help_articles(
1992        self, text: str, size: str, /
1993    ) -> typedefs.JSONObject:
1994        resp = await self._request(
1995            RequestMethod.GET, f"Content/SearchHelpArticles/{text}/{size}/"
1996        )
1997        assert isinstance(resp, dict)
1998        return resp
1999
2000    async def fetch_topics_page(
2001        self,
2002        category_filter: int,
2003        group: int,
2004        date_filter: int,
2005        sort: typing.Union[str, bytes],
2006        *,
2007        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2008        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
2009        tag_filter: undefined.UndefinedOr[str] = undefined.UNDEFINED,
2010    ) -> typedefs.JSONObject:
2011
2012        body: typedefs.JSONObject = {}
2013        if locales is not undefined.UNDEFINED:
2014            body["locales"] = ",".join(str(locales))
2015        else:
2016            body["locales"] = ",".join([])
2017
2018        if tag_filter is not undefined.UNDEFINED:
2019            body["tagstring"] = tag_filter
2020        else:
2021            body["tagstring"] = ""
2022
2023        page = 0 if page is not undefined.UNDEFINED else page
2024
2025        resp = await self._request(
2026            RequestMethod.GET,
2027            f"Forum/GetTopicsPaged/{page}/{0}/{group}/{sort!s}/{date_filter}/{category_filter}/",
2028            json=body,
2029        )
2030        assert isinstance(resp, dict)
2031        return resp
2032
2033    async def fetch_core_topics_page(
2034        self,
2035        category_filter: int,
2036        date_filter: int,
2037        sort: typing.Union[str, bytes],
2038        *,
2039        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2040        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
2041    ) -> typedefs.JSONObject:
2042        body: typedefs.JSONObject = {}
2043
2044        if locales is not undefined.UNDEFINED:
2045            body["locales"] = ",".join(str(locales))
2046        else:
2047            body["locales"] = ",".join([])
2048
2049        resp = await self._request(
2050            RequestMethod.GET,
2051            f"Forum/GetCoreTopicsPaged/{0 if page is undefined.UNDEFINED else page}"
2052            f"/{sort!s}/{date_filter}/{category_filter}/",
2053            json=body,
2054        )
2055        assert isinstance(resp, dict)
2056        return resp
2057
2058    async def fetch_posts_threaded_page(
2059        self,
2060        parent_post: bool,
2061        page: int,
2062        page_size: int,
2063        parent_post_id: int,
2064        reply_size: int,
2065        root_thread_mode: bool,
2066        sort_mode: int,
2067        show_banned: typing.Optional[str] = None,
2068    ) -> typedefs.JSONObject:
2069        resp = await self._request(
2070            RequestMethod.GET,
2071            f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/"
2072            f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/",
2073            json={"showbanned": show_banned},
2074        )
2075        assert isinstance(resp, dict)
2076        return resp
2077
2078    async def fetch_posts_threaded_page_from_child(
2079        self,
2080        child_id: bool,
2081        page: int,
2082        page_size: int,
2083        reply_size: int,
2084        root_thread_mode: bool,
2085        sort_mode: int,
2086        show_banned: typing.Optional[str] = None,
2087    ) -> typedefs.JSONObject:
2088        resp = await self._request(
2089            RequestMethod.GET,
2090            f"Forum/GetPostsThreadedPagedFromChild/{child_id}/"
2091            f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/",
2092            json={"showbanned": show_banned},
2093        )
2094        assert isinstance(resp, dict)
2095        return resp
2096
2097    async def fetch_post_and_parent(
2098        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2099    ) -> typedefs.JSONObject:
2100        resp = await self._request(
2101            RequestMethod.GET,
2102            f"Forum/GetPostAndParent/{child_id}/",
2103            json={"showbanned": show_banned},
2104        )
2105        assert isinstance(resp, dict)
2106        return resp
2107
2108    async def fetch_posts_and_parent_awaiting(
2109        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2110    ) -> typedefs.JSONObject:
2111        resp = await self._request(
2112            RequestMethod.GET,
2113            f"Forum/GetPostAndParentAwaitingApproval/{child_id}/",
2114            json={"showbanned": show_banned},
2115        )
2116        assert isinstance(resp, dict)
2117        return resp
2118
2119    async def fetch_topic_for_content(self, content_id: int, /) -> int:
2120        resp = await self._request(
2121            RequestMethod.GET, f"Forum/GetTopicForContent/{content_id}/"
2122        )
2123        assert isinstance(resp, int)
2124        return resp
2125
2126    async def fetch_forum_tag_suggestions(
2127        self, partial_tag: str, /
2128    ) -> typedefs.JSONObject:
2129        resp = await self._request(
2130            RequestMethod.GET,
2131            "Forum/GetForumTagSuggestions/",
2132            json={"partialtag": partial_tag},
2133        )
2134        assert isinstance(resp, dict)
2135        return resp
2136
2137    async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject:
2138        resp = await self._request(RequestMethod.GET, f"Forum/Poll/{topic_id}/")
2139        assert isinstance(resp, dict)
2140        return resp
2141
2142    async def fetch_recuirement_thread_summaries(self) -> typedefs.JSONArray:
2143        resp = await self._request(RequestMethod.POST, "Forum/Recruit/Summaries/")
2144        assert isinstance(resp, list)
2145        return resp
2146
2147    async def fetch_recommended_groups(
2148        self,
2149        accecss_token: str,
2150        /,
2151        *,
2152        date_range: int = 0,
2153        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
2154    ) -> typedefs.JSONArray:
2155        resp = await self._request(
2156            RequestMethod.POST,
2157            f"GroupV2/Recommended/{int(group_type)}/{date_range}/",
2158            auth=accecss_token,
2159        )
2160        assert isinstance(resp, list)
2161        return resp
2162
2163    async def fetch_available_avatars(self) -> collections.Mapping[str, int]:
2164        resp = await self._request(RequestMethod.GET, "GroupV2/GetAvailableAvatars/")
2165        assert isinstance(resp, dict)
2166        return resp
2167
2168    async def fetch_user_clan_invite_setting(
2169        self,
2170        access_token: str,
2171        /,
2172        membership_type: typedefs.IntAnd[enums.MembershipType],
2173    ) -> bool:
2174        resp = await self._request(
2175            RequestMethod.GET,
2176            f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/",
2177            auth=access_token,
2178        )
2179        assert isinstance(resp, bool)
2180        return resp
2181
2182    async def fetch_banned_group_members(
2183        self, access_token: str, group_id: int, /, *, page: int = 1
2184    ) -> typedefs.JSONObject:
2185        resp = await self._request(
2186            RequestMethod.GET,
2187            f"GroupV2/{group_id}/Banned/?currentpage={page}",
2188            auth=access_token,
2189        )
2190        assert isinstance(resp, dict)
2191        return resp
2192
2193    async def fetch_pending_group_memberships(
2194        self, access_token: str, group_id: int, /, *, current_page: int = 1
2195    ) -> typedefs.JSONObject:
2196        resp = await self._request(
2197            RequestMethod.GET,
2198            f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}",
2199            auth=access_token,
2200        )
2201        assert isinstance(resp, dict)
2202        return resp
2203
2204    async def fetch_invited_group_memberships(
2205        self, access_token: str, group_id: int, /, *, current_page: int = 1
2206    ) -> typedefs.JSONObject:
2207        resp = await self._request(
2208            RequestMethod.GET,
2209            f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}",
2210            auth=access_token,
2211        )
2212        assert isinstance(resp, dict)
2213        return resp
2214
2215    async def invite_member_to_group(
2216        self,
2217        access_token: str,
2218        /,
2219        group_id: int,
2220        membership_id: int,
2221        membership_type: typedefs.IntAnd[enums.MembershipType],
2222        *,
2223        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
2224    ) -> typedefs.JSONObject:
2225        resp = await self._request(
2226            RequestMethod.POST,
2227            f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/",
2228            auth=access_token,
2229            json={"message": str(message)},
2230        )
2231        assert isinstance(resp, dict)
2232        return resp
2233
2234    async def cancel_group_member_invite(
2235        self,
2236        access_token: str,
2237        /,
2238        group_id: int,
2239        membership_id: int,
2240        membership_type: typedefs.IntAnd[enums.MembershipType],
2241    ) -> typedefs.JSONObject:
2242        resp = await self._request(
2243            RequestMethod.POST,
2244            f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/",
2245            auth=access_token,
2246        )
2247        assert isinstance(resp, dict)
2248        return resp
2249
2250    async def fetch_historical_definition(self) -> typedefs.JSONObject:
2251        resp = await self._request(RequestMethod.GET, "Destiny2/Stats/Definition/")
2252        assert isinstance(resp, dict)
2253        return resp
2254
2255    async def fetch_historical_stats(
2256        self,
2257        character_id: int,
2258        membership_id: int,
2259        membership_type: typedefs.IntAnd[enums.MembershipType],
2260        day_start: datetime.datetime,
2261        day_end: datetime.datetime,
2262        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2263        modes: collections.Sequence[typedefs.IntAnd[enums.GameMode]],
2264        *,
2265        period_type: enums.PeriodType = enums.PeriodType.ALL_TIME,
2266    ) -> typedefs.JSONObject:
2267
2268        end, start = time.parse_date_range(day_end, day_start)
2269        resp = await self._request(
2270            RequestMethod.GET,
2271            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/",
2272            json={
2273                "dayend": end,
2274                "daystart": start,
2275                "groups": [str(int(group)) for group in groups],
2276                "modes": [str(int(mode)) for mode in modes],
2277                "periodType": int(period_type),
2278            },
2279        )
2280        assert isinstance(resp, dict)
2281        return resp
2282
2283    async def fetch_historical_stats_for_account(
2284        self,
2285        membership_id: int,
2286        membership_type: typedefs.IntAnd[enums.MembershipType],
2287        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2288    ) -> typedefs.JSONObject:
2289        resp = await self._request(
2290            RequestMethod.GET,
2291            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/",
2292            json={"groups": [str(int(group)) for group in groups]},
2293        )
2294        assert isinstance(resp, dict)
2295        return resp
2296
2297    async def fetch_aggregated_activity_stats(
2298        self,
2299        character_id: int,
2300        membership_id: int,
2301        membership_type: typedefs.IntAnd[enums.MembershipType],
2302        /,
2303    ) -> typedefs.JSONObject:
2304        resp = await self._request(
2305            RequestMethod.GET,
2306            f"Destiny2/{int(membership_type)}/Account/{membership_id}/"
2307            f"Character/{character_id}/Stats/AggregateActivityStats/",
2308        )
2309        assert isinstance(resp, dict)
2310        return resp

A RESTful client implementation for Bungie's API.

This client is designed to only make HTTP requests and return JSON objects to provide RESTful functionality.

This client is also used within aiobungie.Client which deserialize those returned JSON objects using the factory into Pythonic data classes objects which provide Python functionality.

Example
import aiobungie

async def main():
    async with aiobungie.RESTClient("TOKEN") as rest_client:
        req = await rest_client.fetch_clan_members(4389205)
        clan_members = req['results']
        for member in clan_members:
            for k, v in member['destinyUserInfo'].items():
                print(k, v)
Parameters
  • token (str): A valid application token from Bungie's developer portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • client_secret (typing.Optional[str]): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (typing.Optional[int]): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • enable_debugging (bool | str): Whether to enable logging responses or not.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information.
  • "TRACE" | aiobungie.TRACE: This will log the response headers along with the minimal information.
RESTClient( token: str, /, client_secret: Optional[str] = None, client_id: Optional[int] = None, *, max_retries: int = 4, enable_debugging: Union[Literal['TRACE'], bool, int] = False)
403    def __init__(
404        self,
405        token: str,
406        /,
407        client_secret: typing.Optional[str] = None,
408        client_id: typing.Optional[int] = None,
409        *,
410        max_retries: int = 4,
411        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
412    ) -> None:
413        self._session: typing.Optional[_Session] = None
414        self._lock: typing.Optional[asyncio.Lock] = None
415        self._client_secret = client_secret
416        self._client_id = client_id
417        self._token: str = token
418        self._max_retries = max_retries
419        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
420
421        self._set_debug_level(enable_debugging)
client_id: Optional[int]

Return the client id of this REST client if provided, Otherwise None.

metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

A mutable mapping storage for the user's needs.

This mapping is useful for storing any kind of data that the user may need.

Example
import aiobungie

client = aiobungie.RESTClient(…)

async with client:
    # Fetch auth tokens and store them
    client.metadata["tokens"] = await client.fetch_access_token("code")

# Some other time.
async with client:
    # Retrieve the tokens
    tokens: aiobungie.OAuth2Response = client.metadata["tokens"]

    # Use them to fetch your user.
    user = await client.fetch_current_user_memberships(tokens.access_token)
is_alive: bool

Returns True if the REST client is alive and False otherwise.

@typing.final
async def close(self) -> None:
435    @typing.final
436    async def close(self) -> None:
437        session = self._get_session()
438        await session.close()
439        self._session = None

Close this REST client session if it was acquired.

This method is automatically called when using async with contextmanager.

Raises
  • RuntimeError: If the client is already closed.
@typing.final
def open(self) -> None:
441    @typing.final
442    def open(self) -> None:
443        """Open a new client session. This is called internally with contextmanager usage."""
444        if self.is_alive:
445            raise RuntimeError("Cannot open a new session while it's already open.")
446
447        self._session = _Session.create()

Open a new client session. This is called internally with contextmanager usage.

@typing.final
def enable_debugging( self, level: Union[Literal['TRACE'], bool, int] = False, file: Union[pathlib.Path, str, NoneType] = None, /) -> None:
449    @typing.final
450    def enable_debugging(
451        self,
452        level: typing.Union[typing.Literal["TRACE"], bool, int] = False,
453        file: typing.Optional[typing.Union[pathlib.Path, str]] = None,
454        /,
455    ) -> None:
456        self._set_debug_level(level, file)

Enables debugging for the REST calls.

Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information.
  • "TRACE" | aiobungie.TRACE: This will log the response headers along with the minimal information.
Parameters
  • level (str | bool | int): The level of debugging to enable.
  • file (pathlib.Path | str | None): The file path to write the debug logs to. If provided.
@typing.final
async def static_request( self, method: Union[aiobungie.RequestMethod, str], path: str, *, auth: Optional[str] = None, json: Optional[dict[str, Any]] = None) -> Union[dict[str, Any], list[Any], bytes, int, bool, NoneType]:
458    @typing.final
459    async def static_request(
460        self,
461        method: typing.Union[RequestMethod, str],
462        path: str,
463        *,
464        auth: typing.Optional[str] = None,
465        json: typing.Optional[dict[str, typing.Any]] = None,
466    ) -> ResponseSig:
467        return await self._request(method, path, auth=auth, json=json)

Perform an HTTP request given a valid Bungie endpoint.

Parameters
  • method (aiobungie.RequestMethod | str): The request method, This may be GET, POST, PUT, etc.
  • path (str): The Bungie endpoint or path. A path must look something like this Destiny2/3/Profile/46111239123/...
  • auth (str | None): An optional bearer token for methods that requires OAuth2 Authorization header.
  • json (dict[str, typing.Any] | None): An optional JSON data to include in the request.
Returns
  • aiobungie.rest.ResponseSig: The response payload.
@typing.final
def build_oauth2_url( self, client_id: Optional[int] = None) -> Optional[aiobungie.builders.OAuthURL]:
469    @typing.final
470    def build_oauth2_url(
471        self, client_id: typing.Optional[int] = None
472    ) -> typing.Optional[builders.OAuthURL]:
473        client_id = client_id or self._client_id
474        if client_id is None:
475            return None
476
477        return builders.OAuthURL(client_id=client_id)

Builds an OAuth2 URL using the provided user REST/Base client secret/id.

You can't get the complete string URL by using .compile() method.

Parameters
  • client_id (int | None): An optional client id to provide, If left None it will roll back to the id passed to the RESTClient, If both is None this method will return None.
Returns
async def fetch_oauth2_tokens(self, code: str, /) -> aiobungie.builders.OAuth2Response:
694    async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response:
695        if not isinstance(self._client_secret, (str, int)):
696            raise TypeError(
697                "Expected (str, int) for client secret "
698                f"but got {type(self._client_secret).__name__}"  # type: ignore
699            )
700
701        headers = {
702            "client_secret": self._client_secret,
703        }
704
705        data = (
706            f"grant_type=authorization_code&code={code}"
707            f"&client_id={self._client_id}&client_secret={self._client_secret}"
708        )
709
710        response = await self._request(
711            RequestMethod.POST, "", headers=headers, data=data, oauth2=True
712        )
713        assert isinstance(response, dict)
714        return builders.OAuth2Response.build_response(response)

Makes a POST request and fetch the OAuth2 access_token and refresh token.

Parameters
  • code (str): The Authorization code received from the authorization endpoint found in the URL parameters.
Returns
Raises
async def refresh_access_token(self, refresh_token: str, /) -> aiobungie.builders.OAuth2Response:
716    async def refresh_access_token(
717        self, refresh_token: str, /
718    ) -> builders.OAuth2Response:
719        if not isinstance(self._client_secret, (int, str)):
720            raise TypeError(
721                f"Expected (str, int) for client secret but got {type(self._client_secret).__name__}"  # type: ignore
722            )
723
724        data = {
725            "grant_type": "refresh_token",
726            "refresh_token": refresh_token,
727            "client_id": self._client_id,
728            "client_secret": self._client_secret,
729            "Content-Type": "application/x-www-form-urlencoded",
730        }
731
732        response = await self._request(RequestMethod.POST, "", data=data, oauth2=True)
733        assert isinstance(response, dict)
734        return builders.OAuth2Response.build_response(response)

Refresh OAuth2 access token given its refresh token.

Parameters
  • refresh_token (str): The refresh token.
Returns
async def fetch_bungie_user(self, id: int) -> dict[str, typing.Any]:
736    async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject:
737        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
738        resp = await self._request(
739            RequestMethod.GET, f"User/GetBungieNetUserById/{id}/"
740        )
741        assert isinstance(resp, dict)
742        return resp

Fetch a Bungie user by their id.

Parameters
  • id (int): The user id.
Returns
Raises
async def fetch_user_themes(self) -> list[typing.Any]:
744    async def fetch_user_themes(self) -> typedefs.JSONArray:
745        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
746        resp = await self._request(RequestMethod.GET, "User/GetAvailableThemes/")
747        assert isinstance(resp, list)
748        return resp

Fetch all available user themes.

Returns
async def fetch_membership_from_id( self, id: int, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>, /) -> dict[str, typing.Any]:
750    async def fetch_membership_from_id(
751        self,
752        id: int,
753        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
754        /,
755    ) -> typedefs.JSONObject:
756        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
757        resp = await self._request(
758            RequestMethod.GET, f"User/GetMembershipsById/{id}/{int(type)}"
759        )
760        assert isinstance(resp, dict)
761        return resp

Fetch Bungie user's memberships from their id.

Parameters
Returns
Raises
async def fetch_player( self, name: str, code: int, type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>, /) -> list[typing.Any]:
763    async def fetch_player(
764        self,
765        name: str,
766        code: int,
767        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.ALL,
768        /,
769    ) -> typedefs.JSONArray:
770        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
771        resp = await self._request(
772            RequestMethod.POST,
773            f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}",
774            json={"displayName": name, "displayNameCode": code},
775        )
776        assert isinstance(resp, list)
777        return resp

Fetch a Destiny 2 Player.

Parameters
Returns
Raises
async def search_users(self, name: str, /) -> dict[str, typing.Any]:
779    async def search_users(self, name: str, /) -> typedefs.JSONObject:
780        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
781        resp = await self._request(
782            RequestMethod.POST,
783            "User/Search/GlobalName/0",
784            json={"displayNamePrefix": name},
785        )
786        assert isinstance(resp, dict)
787        return resp

Search for users by their global name and return all users who share this name.

Parameters
  • name (str): The user name.
Returns
Raises
async def fetch_clan_from_id( self, id: int, /, access_token: Optional[str] = None) -> dict[str, typing.Any]:
789    async def fetch_clan_from_id(
790        self, id: int, /, access_token: typing.Optional[str] = None
791    ) -> typedefs.JSONObject:
792        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
793        resp = await self._request(
794            RequestMethod.GET, f"GroupV2/{id}", auth=access_token
795        )
796        assert isinstance(resp, dict)
797        return resp

Fetch a Bungie Clan by its id.

Parameters
  • id (int): The clan id.
Other Parameters
  • access_token (typing.Optional[str]): An optional access token to make the request with.

    If the token was bound to a member of the clan, This field aiobungie.crates.Clan.current_user_membership will be available and will return the membership of the user who made this request.

Returns
Raises
async def fetch_clan( self, name: str, /, access_token: Optional[str] = None, *, type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
799    async def fetch_clan(
800        self,
801        name: str,
802        /,
803        access_token: typing.Optional[str] = None,
804        *,
805        type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
806    ) -> typedefs.JSONObject:
807        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
808        resp = await self._request(
809            RequestMethod.GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token
810        )
811        assert isinstance(resp, dict)
812        return resp

Fetch a Clan by its name. This method will return the first clan found with given name name.

Parameters
  • name (str): The clan name.
Other Parameters
Returns
Raises
async def fetch_clan_admins(self, clan_id: int, /) -> dict[str, typing.Any]:
814    async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject:
815        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
816        resp = await self._request(
817            RequestMethod.GET, f"GroupV2/{clan_id}/AdminsAndFounder/"
818        )
819        assert isinstance(resp, dict)
820        return resp

Fetch the admins and founder members of the clan.

Parameters
  • clan_id (int): The clan id.
Returns
Raises
async def fetch_clan_conversations(self, clan_id: int, /) -> list[typing.Any]:
822    async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray:
823        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
824        resp = await self._request(
825            RequestMethod.GET, f"GroupV2/{clan_id}/OptionalConversations/"
826        )
827        assert isinstance(resp, list)
828        return resp

Fetch a clan's conversations.

Parameters
  • clan_id (int): The clan's id.
Returns
async def fetch_application(self, appid: int, /) -> dict[str, typing.Any]:
830    async def fetch_application(self, appid: int, /) -> typedefs.JSONObject:
831        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
832        resp = await self._request(RequestMethod.GET, f"App/Application/{appid}")
833        assert isinstance(resp, dict)
834        return resp

Fetch a Bungie Application.

Parameters
  • appid (int): The application id.
Returns
async def fetch_character( self, member_id: int, membership_type: Union[int, aiobungie.MembershipType], character_id: int, components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> dict[str, typing.Any]:
836    async def fetch_character(
837        self,
838        member_id: int,
839        membership_type: typedefs.IntAnd[enums.MembershipType],
840        character_id: int,
841        components: list[enums.ComponentType],
842        auth: typing.Optional[str] = None,
843    ) -> typedefs.JSONObject:
844        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
845        collector = _collect_components(components)
846        response = await self._request(
847            RequestMethod.GET,
848            f"Destiny2/{int(membership_type)}/Profile/{member_id}/"
849            f"Character/{character_id}/?components={collector}",
850            auth=auth,
851        )
852        assert isinstance(response, dict)
853        return response

Fetch a Destiny 2 player's characters.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
Raises
async def fetch_activities( self, member_id: int, character_id: int, mode: Union[int, aiobungie.GameMode], membership_type: Union[int, aiobungie.MembershipType] = <MembershipType.ALL: -1>, *, page: int = 0, limit: int = 1) -> dict[str, typing.Any]:
855    async def fetch_activities(
856        self,
857        member_id: int,
858        character_id: int,
859        mode: typedefs.IntAnd[enums.GameMode],
860        membership_type: typedefs.IntAnd[
861            enums.MembershipType
862        ] = enums.MembershipType.ALL,
863        *,
864        page: int = 0,
865        limit: int = 1,
866    ) -> typedefs.JSONObject:
867        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
868        resp = await self._request(
869            RequestMethod.GET,
870            f"Destiny2/{int(membership_type)}/Account/"
871            f"{member_id}/Character/{character_id}/Stats/Activities"
872            f"/?mode={int(mode)}&count={limit}&page={page}",
873        )
874        assert isinstance(resp, dict)
875        return resp

Fetch a Destiny 2 activity for the specified user id and character.

Parameters
  • member_id (int): The user id that starts with 4611.
  • character_id (int): The id of the character to retrieve.
  • mode (aiobungie.typedefs.IntAnd[aiobungie.GameMode]): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
Returns
Raises
async def fetch_vendor_sales(self) -> dict[str, typing.Any]:
877    async def fetch_vendor_sales(self) -> typedefs.JSONObject:
878        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
879        resp = await self._request(
880            RequestMethod.GET,
881            f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}",
882        )
883        assert isinstance(resp, dict)
884        return resp
async def fetch_profile( self, membership_id: int, type: Union[int, aiobungie.MembershipType], components: list[aiobungie.ComponentType], auth: Optional[str] = None) -> dict[str, typing.Any]:
886    async def fetch_profile(
887        self,
888        membership_id: int,
889        type: typedefs.IntAnd[enums.MembershipType],
890        components: list[enums.ComponentType],
891        auth: typing.Optional[str] = None,
892    ) -> typedefs.JSONObject:
893        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
894        collector = _collect_components(components)
895        response = await self._request(
896            RequestMethod.GET,
897            f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}",
898            auth=auth,
899        )
900        assert isinstance(response, dict)
901        return response

Fetch a bungie profile.

Parameters
Other Parameters
  • auth (typing.Optional[str]): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
Raises
async def fetch_entity(self, type: str, hash: int) -> dict[str, typing.Any]:
903    async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject:
904        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
905        response = await self._request(
906            RequestMethod.GET, route=f"Destiny2/Manifest/{type}/{hash}"
907        )
908        assert isinstance(response, dict)
909        return response

Fetch a Destiny definition item given its type and hash.

Parameters
  • type (str): Entity's type definition.
  • hash (int): Entity's hash.
Returns
async def fetch_inventory_item(self, hash: int, /) -> dict[str, typing.Any]:
911    async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject:
912        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
913        resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash)
914        assert isinstance(resp, dict)
915        return resp

Fetch a Destiny inventory item entity given a its hash.

Parameters
  • hash (int): Entity's hash.
Returns
async def fetch_objective_entity(self, hash: int, /) -> dict[str, typing.Any]:
917    async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject:
918        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
919        resp = await self.fetch_entity("DestinyObjectiveDefinition", hash)
920        assert isinstance(resp, dict)
921        return resp

Fetch a Destiny objective entity given a its hash.

Parameters
  • hash (int): objective's hash.
Returns
async def fetch_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
923    async def fetch_groups_for_member(
924        self,
925        member_id: int,
926        member_type: typedefs.IntAnd[enums.MembershipType],
927        /,
928        *,
929        filter: int = 0,
930        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
931    ) -> typedefs.JSONObject:
932        resp = await self._request(
933            RequestMethod.GET,
934            f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
935        )
936        assert isinstance(resp, dict)
937        return resp

Fetch the information about the groups for a member.

Parameters
Other Parameters
Returns
async def fetch_potential_groups_for_member( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, filter: int = 0, group_type: Union[int, aiobungie.GroupType] = <GroupType.CLAN: 1>) -> dict[str, typing.Any]:
939    async def fetch_potential_groups_for_member(
940        self,
941        member_id: int,
942        member_type: typedefs.IntAnd[enums.MembershipType],
943        /,
944        *,
945        filter: int = 0,
946        group_type: typedefs.IntAnd[enums.GroupType] = enums.GroupType.CLAN,
947    ) -> typedefs.JSONObject:
948        resp = await self._request(
949            RequestMethod.GET,
950            f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/",
951        )
952        assert isinstance(resp, dict)
953        return resp

Get information about the groups that a given member has applied to or been invited to.

Parameters
Other Parameters
Returns
async def fetch_clan_members( self, clan_id: int, /, *, name: Optional[str] = None, type: Union[int, aiobungie.MembershipType] = <MembershipType.NONE: 0>) -> dict[str, typing.Any]:
955    async def fetch_clan_members(
956        self,
957        clan_id: int,
958        /,
959        *,
960        name: typing.Optional[str] = None,
961        type: typedefs.IntAnd[enums.MembershipType] = enums.MembershipType.NONE,
962    ) -> typedefs.JSONObject:
963        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
964        resp = await self._request(
965            RequestMethod.GET,
966            f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}&currentpage=1",
967        )
968        assert isinstance(resp, dict)
969        return resp

Fetch all Bungie Clan members.

Parameters
  • clan_id (builsins.int): The clans id
Other Parameters
Returns
Raises
async def fetch_hardlinked_credentials( self, credential: int, type: Union[int, aiobungie.CredentialType] = <CredentialType.STEAMID: 12>, /) -> dict[str, typing.Any]:
971    async def fetch_hardlinked_credentials(
972        self,
973        credential: int,
974        type: typedefs.IntAnd[enums.CredentialType] = enums.CredentialType.STEAMID,
975        /,
976    ) -> typedefs.JSONObject:
977        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
978        resp = await self._request(
979            RequestMethod.GET,
980            f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/",
981        )
982        assert isinstance(resp, dict)
983        return resp

Gets any hard linked membership given a credential.

Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now. Cross Save aware.

Parameters
Returns
async def fetch_user_credentials(self, access_token: str, membership_id: int, /) -> list[typing.Any]:
985    async def fetch_user_credentials(
986        self, access_token: str, membership_id: int, /
987    ) -> typedefs.JSONArray:
988        resp = await self._request(
989            RequestMethod.GET,
990            f"User/GetCredentialTypesForTargetAccount/{membership_id}",
991            auth=access_token,
992        )
993        assert isinstance(resp, list)
994        return resp

Fetch an array of credential types attached to the requested account.

This method require OAuth2 Bearer access token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • membership_id (int): The id of the membership to return.
Returns
Raises
async def insert_socket_plug( self, action_token: str, /, instance_id: int, plug: Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]], character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
 996    async def insert_socket_plug(
 997        self,
 998        action_token: str,
 999        /,
1000        instance_id: int,
1001        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1002        character_id: int,
1003        membership_type: typedefs.IntAnd[enums.MembershipType],
1004    ) -> typedefs.JSONObject:
1005
1006        if isinstance(plug, builders.PlugSocketBuilder):
1007            plug = plug.collect()
1008
1009        body = {
1010            "actionToken": action_token,
1011            "itemInstanceId": instance_id,
1012            "plug": plug,
1013            "characterId": character_id,
1014            "membershipType": int(membership_type),
1015        }
1016        resp = await self._request(
1017            RequestMethod.POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body
1018        )
1019        assert isinstance(resp, dict)
1020        return resp

Insert a plug into a socketed item.

OAuth2: AdvancedWriteActions scope is required

Parameters
  • action_token (str): Action token provided by the AwaGetActionToken API call.
  • instance_id (int): The item instance id that's plug inserted.
  • plug (typing.Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
    aiobungie.PlugSocketBuilder()
    .set_socket_array(0)
    .set_socket_index(0)
    .set_plug_item(3023847)
    .collect()
)
await insert_socket_plug_free(..., plug=plug)

character_id : int The character's id. membership_type : aiobungie.typedefs.IntAnd[aiobungie.MembershipType] The membership type.

Returns
Raises
async def insert_socket_plug_free( self, access_token: str, /, instance_id: int, plug: Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]], character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1022    async def insert_socket_plug_free(
1023        self,
1024        access_token: str,
1025        /,
1026        instance_id: int,
1027        plug: typing.Union[builders.PlugSocketBuilder, dict[str, int]],
1028        character_id: int,
1029        membership_type: typedefs.IntAnd[enums.MembershipType],
1030    ) -> typedefs.JSONObject:
1031
1032        if isinstance(plug, builders.PlugSocketBuilder):
1033            plug = plug.collect()
1034
1035        body = {
1036            "itemInstanceId": instance_id,
1037            "plug": plug,
1038            "characterId": character_id,
1039            "membershipType": int(membership_type),
1040        }
1041        resp = await self._request(
1042            RequestMethod.POST,
1043            "Destiny2/Actions/Items/InsertSocketPlugFree",
1044            json=body,
1045            auth=access_token,
1046        )
1047        assert isinstance(resp, dict)
1048        return resp

Insert a plug into a socketed item. This doesn't require an Action token.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • instance_id (int): The item instance id that's plug inserted.
  • plug (typing.Union[aiobungie.builders.PlugSocketBuilder, dict[str, int]]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
    aiobungie.PlugSocketBuilder()
    .set_socket_array(0)
    .set_socket_index(0)
    .set_plug_item(3023847)
    .collect()
)
await insert_socket_plug_free(..., plug=plug)

character_id : int The character's id. membership_type : aiobungie.typedefs.IntAnd[aiobungie.MembershipType] The membership type.

Returns
Raises
async def set_item_lock_state( self, access_token: str, state: bool, /, item_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> int:
1050    async def set_item_lock_state(
1051        self,
1052        access_token: str,
1053        state: bool,
1054        /,
1055        item_id: int,
1056        character_id: int,
1057        membership_type: typedefs.IntAnd[enums.MembershipType],
1058    ) -> int:
1059        body = {
1060            "state": state,
1061            "itemId": item_id,
1062            "characterId": character_id,
1063            "membership_type": int(membership_type),
1064        }
1065        response = await self._request(
1066            RequestMethod.POST,
1067            "Destiny2/Actions/Items/SetLockState",
1068            json=body,
1069            auth=access_token,
1070        )
1071        assert isinstance(response, int)
1072        return response

Set the Lock State for an instanced item.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • state (bool): If True, The item will be locked, If False, The item will be unlocked.
  • item_id (int): The item id.
  • character_id (int): The character id.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type for the associated account.
Returns
  • int: An integer represents whether the request was successful or failed.
Raises
async def set_quest_track_state( self, access_token: str, state: bool, /, item_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> int:
1074    async def set_quest_track_state(
1075        self,
1076        access_token: str,
1077        state: bool,
1078        /,
1079        item_id: int,
1080        character_id: int,
1081        membership_type: typedefs.IntAnd[enums.MembershipType],
1082    ) -> int:
1083        body = {
1084            "state": state,
1085            "itemId": item_id,
1086            "characterId": character_id,
1087            "membership_type": int(membership_type),
1088        }
1089        response = await self._request(
1090            RequestMethod.POST,
1091            "Destiny2/Actions/Items/SetTrackedState",
1092            json=body,
1093            auth=access_token,
1094        )
1095        assert isinstance(response, int)
1096        return response

Set the Tracking State for an instanced Quest or Bounty.

OAuth2: MoveEquipDestinyItems scope is required

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • state (bool): If True, The item will be locked, If False, The item will be unlocked.
  • item_id (int): The item id.
  • character_id (int): The character id.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type for the associated account.
Returns
  • int: An integer represents whether the request was successful or failed.
Raises
async def fetch_manifest_path(self) -> dict[str, typing.Any]:
1098    async def fetch_manifest_path(self) -> typedefs.JSONObject:
1099        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1100        path = await self._request(RequestMethod.GET, "Destiny2/Manifest")
1101        assert isinstance(path, dict)
1102        return path

Fetch the manifest JSON paths.

Returns
  • typedefs.JSONObject: The manifest JSON paths.
async def read_manifest_bytes(self, language: str = 'en', /) -> bytes:
1104    async def read_manifest_bytes(self, language: str = "en", /) -> bytes:
1105        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1106        _ensure_manifest_language(language)
1107
1108        content = await self.fetch_manifest_path()
1109        resp = await self._request(
1110            RequestMethod.GET,
1111            content["mobileWorldContentPaths"][language],
1112            unwrapping="read",
1113            base=True,
1114        )
1115        assert isinstance(resp, bytes)
1116        return resp

Read raw manifest SQLite database bytes response.

This method can be used to write the bytes to zipped file and then extract it to get the manifest content.

Parameters
  • language (str): The manifest database language bytes to get.
Returns
  • bytes: The bytes to read and write the manifest database.
async def download_manifest( self, language: str = 'en', name: str = 'manifest', path: Union[pathlib.Path, str] = '.', *, force: bool = False) -> None:
1118    async def download_manifest(
1119        self,
1120        language: str = "en",
1121        name: str = "manifest",
1122        path: typing.Union[pathlib.Path, str] = ".",
1123        *,
1124        force: bool = False,
1125    ) -> None:
1126        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1127        complete_path = _get_path(name, path, sql=True)
1128
1129        if complete_path.exists() and force:
1130            if force:
1131                _LOG.info(
1132                    f"Found manifest in {complete_path!s}. Forcing to Re-Download."
1133                )
1134                complete_path.unlink(missing_ok=True)
1135
1136                return await self.download_manifest(language, name, path, force=force)
1137
1138            else:
1139                raise FileExistsError(
1140                    "Manifest file already exists, "
1141                    "To force download, set the `force` parameter to `True`."
1142                )
1143
1144        _LOG.info(f"Downloading manifest. Location: {complete_path!s}")
1145        data_bytes = await self.read_manifest_bytes(language)
1146        await asyncio.get_running_loop().run_in_executor(
1147            None, _write_sqlite_bytes, data_bytes, path, name
1148        )

A helper method to download the manifest.

Note

This method downloads the sqlite database and not JSON. Use RESTInterface.download_json_manifest for the JSON version.

Parameters
  • language (str): The manifest language to download, Default is english.
  • path (str | pathlib.Path): The path to save the manifest sqlite database. Example "D:/", Default is the current directory.
  • name (str): The manifest database file name. Default is manifest
  • force (bool): Whether to force the download. Default is False. However if set to true the old file will get removed and a new one will being to download.
Returns
  • None
Raises
  • FileNotFoundError: If the manifest file exists and force is False.
  • ValueError: If the provided language was not recognized.
async def download_json_manifest( self, file_name: str = 'manifest', path: Union[str, pathlib.Path] = '.', language: str = 'en') -> None:
1150    async def download_json_manifest(
1151        self,
1152        file_name: str = "manifest",
1153        path: typing.Union[str, pathlib.Path] = ".",
1154        language: str = "en",
1155    ) -> None:
1156        _ensure_manifest_language(language)
1157
1158        _LOG.info(f"Downloading manifest JSON to {_get_path(file_name, path)!r}...")
1159
1160        content = await self.fetch_manifest_path()
1161        json_bytes = await self._request(
1162            RequestMethod.GET,
1163            content["jsonWorldContentPaths"][language],
1164            unwrapping="read",
1165            base=True,
1166        )
1167
1168        await asyncio.get_running_loop().run_in_executor(
1169            None, _write_json_bytes, json_bytes, file_name, path
1170        )
1171        _LOG.info("Finished downloading manifest JSON.")

Download the Bungie manifest json file.

Parameters
  • file_name (str): The file name to save the manifest json file. Default is manifest.
  • path (str | pathlib.Path): The path to save the manifest json file. Default is the current directory. Example "D:/"
  • language (str): The manifest database language bytes to get. Default is English.
async def fetch_manifest_version(self) -> str:
1173    async def fetch_manifest_version(self) -> str:
1174        return typing.cast(str, (await self.fetch_manifest_path())["version"])

Fetch the manifest version.

Returns
  • str: The manifest version.
async def fetch_linked_profiles( self, member_id: int, member_type: Union[int, aiobungie.MembershipType], /, *, all: bool = False) -> dict[str, typing.Any]:
1176    async def fetch_linked_profiles(
1177        self,
1178        member_id: int,
1179        member_type: typedefs.IntAnd[enums.MembershipType],
1180        /,
1181        *,
1182        all: bool = False,
1183    ) -> typedefs.JSONObject:
1184        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1185        resp = await self._request(
1186            RequestMethod.GET,
1187            f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}",
1188        )
1189        assert isinstance(resp, dict)
1190        return resp

Returns a summary information about all profiles linked to the requested member.

The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.

It will only return linked accounts whose linkages you are allowed to view.

Parameters
Other Parameters
  • all (bool): If provided and set to True, All memberships regardless of whether thry're obscured by overrides will be returned,

    If provided and set to False, Only available memberships will be returned. The default for this is False.

Returns
async def fetch_clan_banners(self) -> dict[str, typing.Any]:
1192    async def fetch_clan_banners(self) -> typedefs.JSONObject:
1193        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1194        resp = await self._request(
1195            RequestMethod.GET, "Destiny2/Clan/ClanBannerDictionary/"
1196        )
1197        assert isinstance(resp, dict)
1198        return resp

Fetch the values of the clan banners.

Returns
async def fetch_public_milestones(self) -> dict[str, typing.Any]:
1200    async def fetch_public_milestones(self) -> typedefs.JSONObject:
1201        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1202        resp = await self._request(RequestMethod.GET, "Destiny2/Milestones/")
1203        assert isinstance(resp, dict)
1204        return resp

Fetch the available milestones.

Returns
async def fetch_public_milestone_content(self, milestone_hash: int, /) -> dict[str, typing.Any]:
1206    async def fetch_public_milestone_content(
1207        self, milestone_hash: int, /
1208    ) -> typedefs.JSONObject:
1209        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1210        resp = await self._request(
1211            RequestMethod.GET, f"Destiny2/Milestones/{milestone_hash}/Content/"
1212        )
1213        assert isinstance(resp, dict)
1214        return resp

Fetch the milestone content given its hash.

Parameters
  • milestone_hash (int): The milestone hash.
Returns
async def fetch_current_user_memberships(self, access_token: str, /) -> dict[str, typing.Any]:
1216    async def fetch_current_user_memberships(
1217        self, access_token: str, /
1218    ) -> typedefs.JSONObject:
1219        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1220        resp = await self._request(
1221            RequestMethod.GET,
1222            "User/GetMembershipsForCurrentUser/",
1223            auth=access_token,
1224        )
1225        assert isinstance(resp, dict)
1226        return resp

Fetch a bungie user's accounts with the signed in user. This GET method requires a Bearer access token for the authorization.

This requires OAuth2 scope enabled and the valid Bearer access_token.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def equip_item( self, access_token: str, /, item_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> None:
1228    async def equip_item(
1229        self,
1230        access_token: str,
1231        /,
1232        item_id: int,
1233        character_id: int,
1234        membership_type: typedefs.IntAnd[enums.MembershipType],
1235    ) -> None:
1236        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1237        payload = {
1238            "itemId": item_id,
1239            "characterId": character_id,
1240            "membershipType": int(membership_type),
1241        }
1242
1243        await self._request(
1244            RequestMethod.POST,
1245            "Destiny2/Actions/Items/EquipItem/",
1246            json=payload,
1247            auth=access_token,
1248        )

Equip an item to a character.

This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item id.
  • character_id (int): The character's id to equip the item to.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type assocaiated with this player.
async def equip_items( self, access_token: str, /, item_ids: list[int], character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> None:
1250    async def equip_items(
1251        self,
1252        access_token: str,
1253        /,
1254        item_ids: list[int],
1255        character_id: int,
1256        membership_type: typedefs.IntAnd[enums.MembershipType],
1257    ) -> None:
1258        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1259        payload = {
1260            "itemIds": item_ids,
1261            "characterId": character_id,
1262            "membershipType": int(membership_type),
1263        }
1264        await self._request(
1265            RequestMethod.POST,
1266            "Destiny2/Actions/Items/EquipItems/",
1267            json=payload,
1268            auth=access_token,
1269        )

Equip multiple items to a character.

This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_ids (list[int]): A list of item ids.
  • character_id (int): The character's id to equip the item to.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The membership type assocaiated with this player.
async def ban_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], *, length: int = 0, comment: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> None:
1271    async def ban_clan_member(
1272        self,
1273        access_token: str,
1274        /,
1275        group_id: int,
1276        membership_id: int,
1277        membership_type: typedefs.IntAnd[enums.MembershipType],
1278        *,
1279        length: int = 0,
1280        comment: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1281    ) -> None:
1282        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1283        payload = {"comment": str(comment), "length": length}
1284        await self._request(
1285            RequestMethod.POST,
1286            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/",
1287            json=payload,
1288            auth=access_token,
1289        )

Bans a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to ban.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Other Parameters
async def unban_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> None:
1291    async def unban_clan_member(
1292        self,
1293        access_token: str,
1294        /,
1295        group_id: int,
1296        membership_id: int,
1297        membership_type: typedefs.IntAnd[enums.MembershipType],
1298    ) -> None:
1299        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1300        await self._request(
1301            RequestMethod.POST,
1302            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/",
1303            auth=access_token,
1304        )

Unbans a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to unban.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
async def kick_clan_member( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1306    async def kick_clan_member(
1307        self,
1308        access_token: str,
1309        /,
1310        group_id: int,
1311        membership_id: int,
1312        membership_type: typedefs.IntAnd[enums.MembershipType],
1313    ) -> typedefs.JSONObject:
1314        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1315        resp = await self._request(
1316            RequestMethod.POST,
1317            f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/",
1318            auth=access_token,
1319        )
1320        assert isinstance(resp, dict)
1321        return resp

Kick a member from the clan.

This request requires OAuth2: oauth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
  • membership_id (int): The member id to kick.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The member's membership type.
Returns
async def edit_clan( self, access_token: str, /, group_id: int, *, name: Optional[str] = None, about: Optional[str] = None, motto: Optional[str] = None, theme: Optional[str] = None, tags: Optional[collections.abc.Sequence[str]] = None, is_public: Optional[bool] = None, locale: Optional[str] = None, avatar_image_index: Optional[int] = None, membership_option: Union[NoneType, int, aiobungie.MembershipOption] = None, allow_chat: Optional[bool] = None, chat_security: Optional[Literal[0, 1]] = None, call_sign: Optional[str] = None, homepage: Optional[Literal[0, 1, 2]] = None, enable_invite_messaging_for_admins: Optional[bool] = None, default_publicity: Optional[Literal[0, 1, 2]] = None, is_public_topic_admin: Optional[bool] = None) -> None:
1323    async def edit_clan(
1324        self,
1325        access_token: str,
1326        /,
1327        group_id: int,
1328        *,
1329        name: typedefs.NoneOr[str] = None,
1330        about: typedefs.NoneOr[str] = None,
1331        motto: typedefs.NoneOr[str] = None,
1332        theme: typedefs.NoneOr[str] = None,
1333        tags: typedefs.NoneOr[collections.Sequence[str]] = None,
1334        is_public: typedefs.NoneOr[bool] = None,
1335        locale: typedefs.NoneOr[str] = None,
1336        avatar_image_index: typedefs.NoneOr[int] = None,
1337        membership_option: typedefs.NoneOr[
1338            typedefs.IntAnd[enums.MembershipOption]
1339        ] = None,
1340        allow_chat: typedefs.NoneOr[bool] = None,
1341        chat_security: typedefs.NoneOr[typing.Literal[0, 1]] = None,
1342        call_sign: typedefs.NoneOr[str] = None,
1343        homepage: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1344        enable_invite_messaging_for_admins: typedefs.NoneOr[bool] = None,
1345        default_publicity: typedefs.NoneOr[typing.Literal[0, 1, 2]] = None,
1346        is_public_topic_admin: typedefs.NoneOr[bool] = None,
1347    ) -> None:
1348        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1349        payload = {
1350            "name": name,
1351            "about": about,
1352            "motto": motto,
1353            "theme": theme,
1354            "tags": tags,
1355            "isPublic": is_public,
1356            "avatarImageIndex": avatar_image_index,
1357            "isPublicTopicAdminOnly": is_public_topic_admin,
1358            "allowChat": allow_chat,
1359            "chatSecurity": chat_security,
1360            "callsign": call_sign,
1361            "homepage": homepage,
1362            "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins,
1363            "defaultPublicity": default_publicity,
1364            "locale": locale,
1365        }
1366        if membership_option is not None:
1367            payload["membershipOption"] = int(membership_option)
1368
1369        await self._request(
1370            RequestMethod.POST,
1371            f"GroupV2/{group_id}/Edit",
1372            json=payload,
1373            auth=access_token,
1374        )

Edit a clan.

Notes
  • This request requires OAuth2: oauth2: AdminGroups scope.
  • All arguments will default to None if not provided. This does not include access_token and group_id
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id to edit.
Other Parameters
async def edit_clan_options( self, access_token: str, /, group_id: int, *, invite_permissions_override: Optional[bool] = None, update_culture_permissionOverride: Optional[bool] = None, host_guided_game_permission_override: Optional[Literal[0, 1, 2]] = None, update_banner_permission_override: Optional[bool] = None, join_level: Union[NoneType, int, aiobungie.ClanMemberType] = None) -> None:
1376    async def edit_clan_options(
1377        self,
1378        access_token: str,
1379        /,
1380        group_id: int,
1381        *,
1382        invite_permissions_override: typedefs.NoneOr[bool] = None,
1383        update_culture_permissionOverride: typedefs.NoneOr[bool] = None,
1384        host_guided_game_permission_override: typedefs.NoneOr[
1385            typing.Literal[0, 1, 2]
1386        ] = None,
1387        update_banner_permission_override: typedefs.NoneOr[bool] = None,
1388        join_level: typedefs.NoneOr[typedefs.IntAnd[enums.ClanMemberType]] = None,
1389    ) -> None:
1390
1391        payload = {
1392            "InvitePermissionOverride": invite_permissions_override,
1393            "UpdateCulturePermissionOverride": update_culture_permissionOverride,
1394            "HostGuidedGamePermissionOverride": host_guided_game_permission_override,
1395            "UpdateBannerPermissionOverride": update_banner_permission_override,
1396            "JoinLevel": int(join_level) if join_level else None,
1397        }
1398
1399        await self._request(
1400            RequestMethod.POST,
1401            f"GroupV2/{group_id}/EditFounderOptions",
1402            json=payload,
1403            auth=access_token,
1404        )

Edit the clan options.

Notes
  • This request requires OAuth2: oauth2: AdminGroups scope.
  • All arguments will default to None if not provided. This does not include access_token and group_id
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group id.
Other Parameters
  • invite_permissions_override (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to invite new members to group Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • update_culture_permissionOverride (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to update group culture Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • host_guided_game_permission_override (aiobungie.typedefs.NoneOr[typing.Literal[0, 1, 2]]): Minimum Member Level allowed to host guided games Always Allowed: Founder, Acting Founder, Admin Allowed Overrides: 0 -> None, 1 -> Beginner 2 -> Member. Default is Member for clans, None for groups, although this means nothing for groups.
  • update_banner_permission_override (aiobungie.typedefs.NoneOr[bool]): Minimum Member Level allowed to update banner Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups.
  • join_level (aiobungie.ClanMemberType): Level to join a member at when accepting an invite, application, or joining an open clan. Default is aiobungie.ClanMemberType.BEGINNER
async def fetch_friends(self, access_token: str, /) -> dict[str, typing.Any]:
1406    async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject:
1407        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1408        resp = await self._request(
1409            RequestMethod.GET,
1410            "Social/Friends/",
1411            auth=access_token,
1412        )
1413        assert isinstance(resp, dict)
1414        return resp

Fetch bungie friend list.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def fetch_friend_requests(self, access_token: str, /) -> dict[str, typing.Any]:
1416    async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject:
1417        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1418        resp = await self._request(
1419            RequestMethod.GET,
1420            "Social/Friends/Requests",
1421            auth=access_token,
1422        )
1423        assert isinstance(resp, dict)
1424        return resp

Fetch pending bungie friend requests queue.

This requests OAuth2: ReadUserData scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
Returns
async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1426    async def accept_friend_request(self, access_token: str, /, member_id: int) -> None:
1427        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1428        await self._request(
1429            RequestMethod.POST,
1430            f"Social/Friends/Requests/Accept/{member_id}",
1431            auth=access_token,
1432        )

Accepts a friend relationship with the target user. The user must be on your incoming friend request list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to accept.
async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1434    async def send_friend_request(self, access_token: str, /, member_id: int) -> None:
1435        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1436        await self._request(
1437            RequestMethod.POST,
1438            f"Social/Friends/Add/{member_id}",
1439            auth=access_token,
1440        )

Requests a friend relationship with the target user.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to send the request to.
async def decline_friend_request(self, access_token: str, /, member_id: int) -> None:
1442    async def decline_friend_request(
1443        self, access_token: str, /, member_id: int
1444    ) -> None:
1445        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1446        await self._request(
1447            RequestMethod.POST,
1448            f"Social/Friends/Requests/Decline/{member_id}",
1449            auth=access_token,
1450        )

Decline a friend request with the target user. The user must be in your incoming friend request list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to decline.
async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1452    async def remove_friend(self, access_token: str, /, member_id: int) -> None:
1453        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1454        await self._request(
1455            RequestMethod.POST,
1456            f"Social/Friends/Remove/{member_id}",
1457            auth=access_token,
1458        )

Removes a friend from your friend list. The user must be in your friend list.

This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to remove.
async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1460    async def remove_friend_request(self, access_token: str, /, member_id: int) -> None:
1461        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1462        await self._request(
1463            RequestMethod.POST,
1464            f"Social/Friends/Requests/Remove/{member_id}",
1465            auth=access_token,
1466        )

Removes a friend from your friend list requests. The user must be in your outgoing request list.

.. note : This request requires OAuth2: BnetWrite scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • member_id (int): The member's id to remove from the requested friend list.
async def approve_all_pending_group_users( self, access_token: str, /, group_id: int, message: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> None:
1468    async def approve_all_pending_group_users(
1469        self,
1470        access_token: str,
1471        /,
1472        group_id: int,
1473        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1474    ) -> None:
1475        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1476        await self._request(
1477            RequestMethod.POST,
1478            f"GroupV2/{group_id}/Members/ApproveAll",
1479            auth=access_token,
1480            json={"message": str(message)},
1481        )

Apporve all pending users for the given group id.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other Parameters
async def deny_all_pending_group_users( self, access_token: str, /, group_id: int, *, message: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> None:
1483    async def deny_all_pending_group_users(
1484        self,
1485        access_token: str,
1486        /,
1487        group_id: int,
1488        *,
1489        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1490    ) -> None:
1491        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1492        await self._request(
1493            RequestMethod.POST,
1494            f"GroupV2/{group_id}/Members/DenyAll",
1495            auth=access_token,
1496            json={"message": str(message)},
1497        )

Deny all pending users for the given group id.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other Parameters
async def add_optional_conversation( self, access_token: str, /, group_id: int, *, name: Union[aiobungie.UndefinedType, str] = UNDEFINED, security: Literal[0, 1] = 0) -> None:
1499    async def add_optional_conversation(
1500        self,
1501        access_token: str,
1502        /,
1503        group_id: int,
1504        *,
1505        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1506        security: typing.Literal[0, 1] = 0,
1507    ) -> None:
1508        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1509        payload = {"chatName": str(name), "chatSecurity": security}
1510        await self._request(
1511            RequestMethod.POST,
1512            f"GroupV2/{group_id}/OptionalConversations/Add",
1513            json=payload,
1514            auth=access_token,
1515        )

Add a new chat channel to a group.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
Other parameters

name: aiobungie.UndefinedOr[str] The chat name. Default to UNDEFINED security: typing.Literal[0, 1] The security level of the chat.

If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`
async def edit_optional_conversation( self, access_token: str, /, group_id: int, conversation_id: int, *, name: Union[aiobungie.UndefinedType, str] = UNDEFINED, security: Literal[0, 1] = 0, enable_chat: bool = False) -> None:
1517    async def edit_optional_conversation(
1518        self,
1519        access_token: str,
1520        /,
1521        group_id: int,
1522        conversation_id: int,
1523        *,
1524        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1525        security: typing.Literal[0, 1] = 0,
1526        enable_chat: bool = False,
1527    ) -> None:
1528        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1529        payload = {
1530            "chatEnabled": enable_chat,
1531            "chatName": str(name),
1532            "chatSecurity": security,
1533        }
1534        await self._request(
1535            RequestMethod.POST,
1536            f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}",
1537            json=payload,
1538            auth=access_token,
1539        )

Edit the settings of this chat channel.

This request requires OAuth2: AdminGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The given group id.
  • conversation_id (int): The conversation/chat id.
Other parameters

name: aiobungie.UndefinedOr[str] The new chat name. Default to UNDEFINED security: typing.Literal[0, 1] The new security level of the chat.

If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`

enable_chat : bool Whether to enable chatting or not. If set to True then chatting will be enabled. Otherwise it will be disabled.

async def transfer_item( self, access_token: str, /, item_id: int, item_hash: int, character_id: int, member_type: Union[int, aiobungie.MembershipType], *, stack_size: int = 1, vault: bool = False) -> None:
1541    async def transfer_item(
1542        self,
1543        access_token: str,
1544        /,
1545        item_id: int,
1546        item_hash: int,
1547        character_id: int,
1548        member_type: typedefs.IntAnd[enums.MembershipType],
1549        *,
1550        stack_size: int = 1,
1551        vault: bool = False,
1552    ) -> None:
1553        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1554        payload = {
1555            "characterId": character_id,
1556            "membershipType": int(member_type),
1557            "itemId": item_id,
1558            "itemReferenceHash": item_hash,
1559            "stackSize": stack_size,
1560            "transferToVault": vault,
1561        }
1562        await self._request(
1563            RequestMethod.POST,
1564            "Destiny2/Actions/Items/TransferItem",
1565            json=payload,
1566            auth=access_token,
1567        )

Transfer an item from / to your vault.

Notes
  • This method requires OAuth2: MoveEquipDestinyItems scope.
  • This method requires both item id and hash.
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item instance id you to transfer.
  • item_hash (int): The item hash.
  • character_id (int): The character id to transfer the item from/to.
  • member_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The user membership type.
Other Parameters
  • stack_size (int): The item stack size.
  • valut (bool): Whether to trasnfer this item to your valut or not. Defaults to False.
async def pull_item( self, access_token: str, /, item_id: int, item_hash: int, character_id: int, member_type: Union[int, aiobungie.MembershipType], *, stack_size: int = 1, vault: bool = False) -> None:
1569    async def pull_item(
1570        self,
1571        access_token: str,
1572        /,
1573        item_id: int,
1574        item_hash: int,
1575        character_id: int,
1576        member_type: typedefs.IntAnd[enums.MembershipType],
1577        *,
1578        stack_size: int = 1,
1579        vault: bool = False,
1580    ) -> None:
1581        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>
1582        payload = {
1583            "characterId": character_id,
1584            "membershipType": int(member_type),
1585            "itemId": item_id,
1586            "itemReferenceHash": item_hash,
1587            "stackSize": stack_size,
1588            "transferToVault": vault,
1589        }
1590        await self._request(
1591            RequestMethod.POST,
1592            "Destiny2/Actions/Items/PullFromPostmaster",
1593            json=payload,
1594            auth=access_token,
1595        )

pull an item from the postmaster.

Notes
  • This method requires OAuth2: MoveEquipDestinyItems scope.
  • This method requires both item id and hash.
Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • item_id (int): The item instance id to pull.
  • item_hash (int): The item hash.
  • character_id (int): The character id to pull the item to.
  • member_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The user membership type.
Other Parameters
  • stack_size (int): The item stack size.
  • valut (bool): Whether to pill this item to your valut or not. Defaults to False.
async def fetch_fireteams( self, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform] = <FireteamPlatform.ANY: 0>, language: Union[aiobungie.FireteamLanguage, str] = <FireteamLanguage.ALL: >, date_range: Union[int, aiobungie.FireteamDate] = <FireteamDate.ALL: 0>, page: int = 0, slots_filter: int = 0) -> dict[str, typing.Any]:
1597    async def fetch_fireteams(
1598        self,
1599        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1600        *,
1601        platform: typedefs.IntAnd[
1602            fireteams.FireteamPlatform
1603        ] = fireteams.FireteamPlatform.ANY,
1604        language: typing.Union[
1605            fireteams.FireteamLanguage, str
1606        ] = fireteams.FireteamLanguage.ALL,
1607        date_range: typedefs.IntAnd[
1608            fireteams.FireteamDate
1609        ] = fireteams.FireteamDate.ALL,
1610        page: int = 0,
1611        slots_filter: int = 0,
1612    ) -> typedefs.JSONObject:
1613        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1614        resp = await self._request(
1615            RequestMethod.GET,
1616            f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}",  # noqa: E501 Line too long
1617        )
1618        assert isinstance(resp, dict)
1619        return resp

Fetch public Bungie fireteams with open slots.

Parameters
Other Parameters
Returns
async def fetch_avaliable_clan_fireteams( self, access_token: str, group_id: int, activity_type: Union[int, aiobungie.FireteamActivity], *, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], date_range: Union[int, aiobungie.FireteamDate] = <FireteamDate.ALL: 0>, page: int = 0, public_only: bool = False, slots_filter: int = 0) -> dict[str, typing.Any]:
1621    async def fetch_avaliable_clan_fireteams(
1622        self,
1623        access_token: str,
1624        group_id: int,
1625        activity_type: typedefs.IntAnd[fireteams.FireteamActivity],
1626        *,
1627        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1628        language: typing.Union[fireteams.FireteamLanguage, str],
1629        date_range: typedefs.IntAnd[
1630            fireteams.FireteamDate
1631        ] = fireteams.FireteamDate.ALL,
1632        page: int = 0,
1633        public_only: bool = False,
1634        slots_filter: int = 0,
1635    ) -> typedefs.JSONObject:
1636        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1637        resp = await self._request(
1638            RequestMethod.GET,
1639            f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}",  # noqa: E501
1640            json={"langFilter": str(language)},
1641            auth=access_token,
1642        )
1643        assert isinstance(resp, dict)
1644        return resp

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id of the fireteam.
  • activity_type (aiobungie.typedefs.IntAnd[aiobungie.crates.FireteamActivity]): The fireteam activity type.
Other Parameters
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • date_range (aiobungie.typedefs.IntAnd[aiobungie.FireteamDate]): An integer to filter the date range of the returned fireteams. Defaults to aiobungie.FireteamDate.ALL.
  • page (int): The page number. By default its 0 which returns all available activities.
  • public_only (bool): If set to True, Then only public fireteams will be returned.
  • slots_filter (int): Filter the returned fireteams based on available slots. Default is 0
Returns
async def fetch_clan_fireteam( self, access_token: str, fireteam_id: int, group_id: int) -> dict[str, typing.Any]:
1646    async def fetch_clan_fireteam(
1647        self, access_token: str, fireteam_id: int, group_id: int
1648    ) -> typedefs.JSONObject:
1649        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1650        resp = await self._request(
1651            RequestMethod.GET,
1652            f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}",
1653            auth=access_token,
1654        )
1655        assert isinstance(resp, dict)
1656        return resp

Fetch a specific clan fireteam.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch the fireteam from.
  • fireteam_id (int): The fireteam id to fetch.
Returns
async def fetch_my_clan_fireteams( self, access_token: str, group_id: int, *, include_closed: bool = True, platform: Union[int, aiobungie.FireteamPlatform], language: Union[aiobungie.FireteamLanguage, str], filtered: bool = True, page: int = 0) -> dict[str, typing.Any]:
1658    async def fetch_my_clan_fireteams(
1659        self,
1660        access_token: str,
1661        group_id: int,
1662        *,
1663        include_closed: bool = True,
1664        platform: typedefs.IntAnd[fireteams.FireteamPlatform],
1665        language: typing.Union[fireteams.FireteamLanguage, str],
1666        filtered: bool = True,
1667        page: int = 0,
1668    ) -> typedefs.JSONObject:
1669        payload = {"groupFilter": filtered, "langFilter": str(language)}
1670        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1671        resp = await self._request(
1672            RequestMethod.GET,
1673            f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}",
1674            json=payload,
1675            auth=access_token,
1676        )
1677        assert isinstance(resp, dict)
1678        return resp

Fetch a clan's fireteams with open slots.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id to fetch.
Other Parameters
  • include_closed (bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True.
  • platform (aiobungie.typedefs.IntAnd[aiobungie.FireteamPlatform]): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
  • language (typing.Union[aiobungie.FireteamLanguage, str]): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL
  • filtered (bool): If set to True, it will filter by clan. Otherwise not. Default is True.
  • page (int): The page number. By default its 0 which returns all available activities.
Returns
async def fetch_private_clan_fireteams(self, access_token: str, group_id: int, /) -> int:
1680    async def fetch_private_clan_fireteams(
1681        self, access_token: str, group_id: int, /
1682    ) -> int:
1683        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1684        resp = await self._request(
1685            RequestMethod.GET,
1686            f"Fireteam/Clan/{group_id}/ActiveCount",
1687            auth=access_token,
1688        )
1689        assert isinstance(resp, int)
1690        return resp

Fetch the active count of the clan fireteams that are only private.

This method requires OAuth2: ReadGroups scope.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • group_id (int): The group/clan id.
Returns
  • int: The active fireteams count. Max value returned is 25.
async def fetch_post_activity(self, instance_id: int, /) -> dict[str, typing.Any]:
1692    async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject:
1693        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1694        resp = await self._request(
1695            RequestMethod.GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}"
1696        )
1697        assert isinstance(resp, dict)
1698        return resp

Fetch a post activity details.

Parameters
  • instance_id (int): The activity instance id.
Returns
async def search_entities( self, name: str, entity_type: str, *, page: int = 0) -> dict[str, typing.Any]:
1700    async def search_entities(
1701        self, name: str, entity_type: str, *, page: int = 0
1702    ) -> typedefs.JSONObject:
1703        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1704        resp = await self._request(
1705            RequestMethod.GET,
1706            f"Destiny2/Armory/Search/{entity_type}/{name}/",
1707            json={"page": page},
1708        )
1709        assert isinstance(resp, dict)
1710        return resp

Search for Destiny2 entities given a name and its type.

Parameters
  • name (str): The name of the entity, i.e., Thunderlord, One thousand voices.
  • entity_type (str): The type of the entity, AKA Definition, For an example DestinyInventoryItemDefinition
Other Parameters
  • page (int): An optional page to return. Default to 0.
Returns
async def fetch_unique_weapon_history( self, membership_id: int, character_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
1712    async def fetch_unique_weapon_history(
1713        self,
1714        membership_id: int,
1715        character_id: int,
1716        membership_type: typedefs.IntAnd[enums.MembershipType],
1717    ) -> typedefs.JSONObject:
1718        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1719        resp = await self._request(
1720            RequestMethod.GET,
1721            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/",
1722        )
1723        assert isinstance(resp, dict)
1724        return resp

Fetch details about unique weapon usage for a character. Includes all exotics.

Parameters
Returns
async def fetch_item( self, member_id: int, item_id: int, membership_type: Union[int, aiobungie.MembershipType], components: list[aiobungie.ComponentType]) -> dict[str, typing.Any]:
1726    async def fetch_item(
1727        self,
1728        member_id: int,
1729        item_id: int,
1730        membership_type: typedefs.IntAnd[enums.MembershipType],
1731        components: list[enums.ComponentType],
1732    ) -> typedefs.JSONObject:
1733        collector = _collect_components(components)
1734        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1735        resp = await self._request(
1736            RequestMethod.GET,
1737            f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}",
1738        )
1739        assert isinstance(resp, dict)
1740        return resp

Fetch an instanced Destiny 2 item's details.

Parameters
Returns
async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> dict[str, typing.Any]:
1742    async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject:
1743        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1744        resp = await self._request(
1745            RequestMethod.GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/"
1746        )
1747        assert isinstance(resp, dict)
1748        return resp

Fetch the weekly reward state for a clan.

Parameters
  • clan_id (int): The clan id.
Returns
async def fetch_available_locales(self) -> dict[str, typing.Any]:
1750    async def fetch_available_locales(self) -> typedefs.JSONObject:
1751        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1752        resp = await self._request(
1753            RequestMethod.GET, "Destiny2/Manifest/DestinyLocaleDefinition/"
1754        )
1755        assert isinstance(resp, dict)
1756        return resp

Fetch available locales at Bungie.

Returns
async def fetch_common_settings(self) -> dict[str, typing.Any]:
1758    async def fetch_common_settings(self) -> typedefs.JSONObject:
1759        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1760        resp = await self._request(RequestMethod.GET, "Settings")
1761        assert isinstance(resp, dict)
1762        return resp

Fetch the common settings used by Bungie's envirotment.

Returns
async def fetch_user_systems_overrides(self) -> dict[str, typing.Any]:
1764    async def fetch_user_systems_overrides(self) -> typedefs.JSONObject:
1765        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1766        resp = await self._request(RequestMethod.GET, "UserSystemOverrides")
1767        assert isinstance(resp, dict)
1768        return resp

Fetch a user's specific system overrides.

Returns
async def fetch_global_alerts(self, *, include_streaming: bool = False) -> list[typing.Any]:
1770    async def fetch_global_alerts(
1771        self, *, include_streaming: bool = False
1772    ) -> typedefs.JSONArray:
1773        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1774        resp = await self._request(
1775            RequestMethod.GET, f"GlobalAlerts/?includestreaming={include_streaming}"
1776        )
1777        assert isinstance(resp, list)
1778        return resp

Fetch any active global alerts.

Parameters
  • include_streaming (bool): If True, the returned results will include streaming alerts. Default is False.
Returns
async def awainitialize_request( self, access_token: str, type: Literal[0, 1], membership_type: Union[int, aiobungie.MembershipType], /, *, affected_item_id: Optional[int] = None, character_id: Optional[int] = None) -> dict[str, typing.Any]:
1780    async def awainitialize_request(
1781        self,
1782        access_token: str,
1783        type: typing.Literal[0, 1],
1784        membership_type: typedefs.IntAnd[enums.MembershipType],
1785        /,
1786        *,
1787        affected_item_id: typing.Optional[int] = None,
1788        character_id: typing.Optional[int] = None,
1789    ) -> typedefs.JSONObject:
1790        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1791
1792        body = {"type": type, "membershipType": int(membership_type)}
1793
1794        if affected_item_id is not None:
1795            body["affectedItemId"] = affected_item_id
1796
1797        if character_id is not None:
1798            body["characterId"] = character_id
1799
1800        resp = await self._request(
1801            RequestMethod.POST, "Destiny2/Awa/Initialize", json=body, auth=access_token
1802        )
1803        assert isinstance(resp, dict)
1804        return resp

Initialize a request to perform an advanced write action.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • type (typing.Literal[0, 1]): Type of the advanced write action. Its either 0 or 1. If set to 0 that means it None. Otherwise if 1 that means its insert plugs.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type of the account to modify.
Other Parameters
  • affected_item_id (typing.Optional[int]): Item instance ID the action shall be applied to. This is optional for all but a new AwaType values.
  • character_id (typing.Optional[int]): The Destiny character ID to perform this action on.
Returns
async def awaget_action_token(self, access_token: str, correlation_id: str, /) -> dict[str, typing.Any]:
1806    async def awaget_action_token(
1807        self, access_token: str, correlation_id: str, /
1808    ) -> typedefs.JSONObject:
1809        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1810        resp = await self._request(
1811            RequestMethod.POST,
1812            f"Destiny2/Awa/GetActionToken/{correlation_id}",
1813            auth=access_token,
1814        )
1815        assert isinstance(resp, dict)
1816        return resp

Returns the action token if user approves the request.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • correlation_id (str): The identifier for the advanced write action request.
Returns
async def awa_provide_authorization_result( self, access_token: str, selection: int, correlation_id: str, nonce: collections.abc.MutableSequence[typing.Union[str, bytes]]) -> int:
1818    async def awa_provide_authorization_result(
1819        self,
1820        access_token: str,
1821        selection: int,
1822        correlation_id: str,
1823        nonce: collections.MutableSequence[typing.Union[str, bytes]],
1824    ) -> int:
1825        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1826
1827        body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce}
1828
1829        resp = await self._request(
1830            RequestMethod.POST,
1831            "Destiny2/Awa/AwaProvideAuthorizationResult",
1832            json=body,
1833            auth=access_token,
1834        )
1835        assert isinstance(resp, int)
1836        return resp

Provide the result of the user interaction. Called by the Bungie Destiny App to approve or reject a request.

OAuth2: AdvancedWriteActions application scope is required to perform this request.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • selection (int): Indication of the selection the user has made (Approving or rejecting the action)
  • correlation_id (str): Correlation ID of the request.
  • nonce (collections.MutableSequence[str, bytes]): Secret nonce received via the PUSH notification.
Returns
  • int: ...
async def fetch_vendors( self, access_token: str, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], /, components: list[aiobungie.ComponentType], filter: Optional[int] = None) -> dict[str, typing.Any]:
1838    async def fetch_vendors(
1839        self,
1840        access_token: str,
1841        character_id: int,
1842        membership_id: int,
1843        membership_type: typedefs.IntAnd[enums.MembershipType],
1844        /,
1845        components: list[enums.ComponentType],
1846        filter: typing.Optional[int] = None,
1847    ) -> typedefs.JSONObject:
1848        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1849        components_ = _collect_components(components)
1850        route = (
1851            f"Destiny2/{int(membership_type)}/Profile/{membership_id}"
1852            f"/Character/{character_id}/Vendors/?components={components_}"
1853        )
1854
1855        if filter is not None:
1856            route = route + f"&filter={filter}"
1857
1858        resp = await self._request(
1859            RequestMethod.GET,
1860            route,
1861            auth=access_token,
1862        )
1863        assert isinstance(resp, dict)
1864        return resp

Get currently available vendors from the list of vendors that can possibly have rotating inventory.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • character_id (int): The character ID to return the vendor info for.
  • membership_id (int): The Destiny membership id to return the vendor info for.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type to return the vendor info for.
  • components (list[aiobungie.ComponentType]): A list of vendor components to collect and return.
Other Parameters
  • filter (int): Filters the type of items returned from the vendor. This can be left to None.
Returns
async def fetch_vendor( self, access_token: str, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], vendor_hash: int, /, components: list[aiobungie.ComponentType]) -> dict[str, typing.Any]:
1866    async def fetch_vendor(
1867        self,
1868        access_token: str,
1869        character_id: int,
1870        membership_id: int,
1871        membership_type: typedefs.IntAnd[enums.MembershipType],
1872        vendor_hash: int,
1873        /,
1874        components: list[enums.ComponentType],
1875    ) -> typedefs.JSONObject:
1876        # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>>.
1877        components_ = _collect_components(components)
1878        resp = await self._request(
1879            RequestMethod.GET,
1880            (
1881                f"Platform/Destiny2/{int(membership_type)}/Profile/{membership_id}"
1882                f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}"
1883            ),
1884            auth=access_token,
1885        )
1886        assert isinstance(resp, dict)
1887        return resp

Fetch details for a specific vendor.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • character_id (int): The character ID to return the vendor info for.
  • membership_id (int): The Destiny membership id to return the vendor info for.
  • membership_type (aiobungie.typedefs.IntAnd[aiobungie.MembershipType]): The Destiny membership type to return the vendor info for.
  • vendor_hash (int): The vendor hash to return the details for.
  • components (list[aiobungie.ComponentType]): A list of vendor components to collect and return.
Returns
async def fetch_application_api_usage( self, access_token: str, application_id: int, /, *, start: Optional[datetime.datetime] = None, end: Optional[datetime.datetime] = None) -> dict[str, typing.Any]:
1889    async def fetch_application_api_usage(
1890        self,
1891        access_token: str,
1892        application_id: int,
1893        /,
1894        *,
1895        start: typing.Optional[datetime.datetime] = None,
1896        end: typing.Optional[datetime.datetime] = None,
1897    ) -> typedefs.JSONObject:
1898
1899        end_date, start_date = time.parse_date_range(end, start)
1900        resp = await self._request(
1901            RequestMethod.GET,
1902            f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}",
1903            auth=access_token,
1904        )
1905        assert isinstance(resp, dict)
1906        return resp

Fetch a Bungie application's API usage.

Parameters
  • access_token (str): The bearer access token associated with the bungie account.
  • application_id (int): The application id to get.
Other Parameters
  • start (typing.Optional[datetime.datetime]): A datetime object can be used to collect the start of the application usage. This is limited and can go back to 30 days maximum.

    If this is left to None. It will return the last 24 hours.

  • end (typing.Optional[datetime.datetime]): A datetime object can be used to collect the end of the application usage.

    If this is left to None. It will return now.

Example
import datetime

# Fetch data from 2021 Dec 10th to 2021 Dec 20th
await fetch_application_api_usage(
    start=datetime.datetime(2021, 12, 10), end=datetime.datetime(2021, 12, 20)
)
Returns
async def fetch_bungie_applications(self) -> list[typing.Any]:
1908    async def fetch_bungie_applications(self) -> typedefs.JSONArray:
1909        resp = await self._request(RequestMethod.GET, "App/FirstParty")
1910        assert isinstance(resp, list)
1911        return resp

Fetch details for applications created by Bungie.

Returns
async def fetch_content_type(self, type: str, /) -> dict[str, typing.Any]:
1913    async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject:
1914        resp = await self._request(RequestMethod.GET, f"Content/GetContentType/{type}/")
1915        assert isinstance(resp, dict)
1916        return resp
async def fetch_content_by_id( self, id: int, locale: str, /, *, head: bool = False) -> dict[str, typing.Any]:
1918    async def fetch_content_by_id(
1919        self, id: int, locale: str, /, *, head: bool = False
1920    ) -> typedefs.JSONObject:
1921        resp = await self._request(
1922            RequestMethod.GET,
1923            f"Content/GetContentById/{id}/{locale}/",
1924            json={"head": head},
1925        )
1926        assert isinstance(resp, dict)
1927        return resp
async def fetch_content_by_tag_and_type( self, locale: str, tag: str, type: str, *, head: bool = False) -> dict[str, typing.Any]:
1929    async def fetch_content_by_tag_and_type(
1930        self, locale: str, tag: str, type: str, *, head: bool = False
1931    ) -> typedefs.JSONObject:
1932        resp = await self._request(
1933            RequestMethod.GET,
1934            f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/",
1935            json={"head": head},
1936        )
1937        assert isinstance(resp, dict)
1938        return resp
async def search_content_with_text( self, locale: str, /, content_type: str, search_text: str, tag: str, *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED, source: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
1940    async def search_content_with_text(
1941        self,
1942        locale: str,
1943        /,
1944        content_type: str,
1945        search_text: str,
1946        tag: str,
1947        *,
1948        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1949        source: undefined.UndefinedOr[str] = undefined.UNDEFINED,
1950    ) -> typedefs.JSONObject:
1951
1952        body: typedefs.JSONObject = {}
1953
1954        body["ctype"] = content_type
1955        body["searchtext"] = search_text
1956        body["tag"] = tag
1957
1958        if page is not undefined.UNDEFINED:
1959            body["currentpage"] = page
1960        else:
1961            body["currentpage"] = 1
1962
1963        if source is not undefined.UNDEFINED:
1964            body["source"] = source
1965        else:
1966            source = ""
1967        resp = await self._request(
1968            RequestMethod.GET, f"Content/Search/{locale}/", json=body
1969        )
1970        assert isinstance(resp, dict)
1971        return resp
async def search_content_by_tag_and_type( self, locale: str, tag: str, type: str, *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED) -> dict[str, typing.Any]:
1973    async def search_content_by_tag_and_type(
1974        self,
1975        locale: str,
1976        tag: str,
1977        type: str,
1978        *,
1979        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
1980    ) -> typedefs.JSONObject:
1981        body: typedefs.JSONObject = {}
1982        body["currentpage"] = 1 if page is undefined.UNDEFINED else page
1983        resp = await self._request(
1984            RequestMethod.GET,
1985            f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/",
1986            json=body,
1987        )
1988        assert isinstance(resp, dict)
1989        return resp
async def search_help_articles(self, text: str, size: str, /) -> dict[str, typing.Any]:
1991    async def search_help_articles(
1992        self, text: str, size: str, /
1993    ) -> typedefs.JSONObject:
1994        resp = await self._request(
1995            RequestMethod.GET, f"Content/SearchHelpArticles/{text}/{size}/"
1996        )
1997        assert isinstance(resp, dict)
1998        return resp
async def fetch_topics_page( self, category_filter: int, group: int, date_filter: int, sort: Union[str, bytes], *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED, locales: Union[aiobungie.UndefinedType, collections.abc.Iterable[str]] = UNDEFINED, tag_filter: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
2000    async def fetch_topics_page(
2001        self,
2002        category_filter: int,
2003        group: int,
2004        date_filter: int,
2005        sort: typing.Union[str, bytes],
2006        *,
2007        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2008        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
2009        tag_filter: undefined.UndefinedOr[str] = undefined.UNDEFINED,
2010    ) -> typedefs.JSONObject:
2011
2012        body: typedefs.JSONObject = {}
2013        if locales is not undefined.UNDEFINED:
2014            body["locales"] = ",".join(str(locales))
2015        else:
2016            body["locales"] = ",".join([])
2017
2018        if tag_filter is not undefined.UNDEFINED:
2019            body["tagstring"] = tag_filter
2020        else:
2021            body["tagstring"] = ""
2022
2023        page = 0 if page is not undefined.UNDEFINED else page
2024
2025        resp = await self._request(
2026            RequestMethod.GET,
2027            f"Forum/GetTopicsPaged/{page}/{0}/{group}/{sort!s}/{date_filter}/{category_filter}/",
2028            json=body,
2029        )
2030        assert isinstance(resp, dict)
2031        return resp
async def fetch_core_topics_page( self, category_filter: int, date_filter: int, sort: Union[str, bytes], *, page: Union[aiobungie.UndefinedType, int] = UNDEFINED, locales: Union[aiobungie.UndefinedType, collections.abc.Iterable[str]] = UNDEFINED) -> dict[str, typing.Any]:
2033    async def fetch_core_topics_page(
2034        self,
2035        category_filter: int,
2036        date_filter: int,
2037        sort: typing.Union[str, bytes],
2038        *,
2039        page: undefined.UndefinedOr[int] = undefined.UNDEFINED,
2040        locales: undefined.UndefinedOr[collections.Iterable[str]] = undefined.UNDEFINED,
2041    ) -> typedefs.JSONObject:
2042        body: typedefs.JSONObject = {}
2043
2044        if locales is not undefined.UNDEFINED:
2045            body["locales"] = ",".join(str(locales))
2046        else:
2047            body["locales"] = ",".join([])
2048
2049        resp = await self._request(
2050            RequestMethod.GET,
2051            f"Forum/GetCoreTopicsPaged/{0 if page is undefined.UNDEFINED else page}"
2052            f"/{sort!s}/{date_filter}/{category_filter}/",
2053            json=body,
2054        )
2055        assert isinstance(resp, dict)
2056        return resp
async def fetch_posts_threaded_page( self, parent_post: bool, page: int, page_size: int, parent_post_id: int, reply_size: int, root_thread_mode: bool, sort_mode: int, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2058    async def fetch_posts_threaded_page(
2059        self,
2060        parent_post: bool,
2061        page: int,
2062        page_size: int,
2063        parent_post_id: int,
2064        reply_size: int,
2065        root_thread_mode: bool,
2066        sort_mode: int,
2067        show_banned: typing.Optional[str] = None,
2068    ) -> typedefs.JSONObject:
2069        resp = await self._request(
2070            RequestMethod.GET,
2071            f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/"
2072            f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/",
2073            json={"showbanned": show_banned},
2074        )
2075        assert isinstance(resp, dict)
2076        return resp
async def fetch_posts_threaded_page_from_child( self, child_id: bool, page: int, page_size: int, reply_size: int, root_thread_mode: bool, sort_mode: int, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2078    async def fetch_posts_threaded_page_from_child(
2079        self,
2080        child_id: bool,
2081        page: int,
2082        page_size: int,
2083        reply_size: int,
2084        root_thread_mode: bool,
2085        sort_mode: int,
2086        show_banned: typing.Optional[str] = None,
2087    ) -> typedefs.JSONObject:
2088        resp = await self._request(
2089            RequestMethod.GET,
2090            f"Forum/GetPostsThreadedPagedFromChild/{child_id}/"
2091            f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/",
2092            json={"showbanned": show_banned},
2093        )
2094        assert isinstance(resp, dict)
2095        return resp
async def fetch_post_and_parent( self, child_id: int, /, *, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2097    async def fetch_post_and_parent(
2098        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2099    ) -> typedefs.JSONObject:
2100        resp = await self._request(
2101            RequestMethod.GET,
2102            f"Forum/GetPostAndParent/{child_id}/",
2103            json={"showbanned": show_banned},
2104        )
2105        assert isinstance(resp, dict)
2106        return resp
async def fetch_posts_and_parent_awaiting( self, child_id: int, /, *, show_banned: Optional[str] = None) -> dict[str, typing.Any]:
2108    async def fetch_posts_and_parent_awaiting(
2109        self, child_id: int, /, *, show_banned: typing.Optional[str] = None
2110    ) -> typedefs.JSONObject:
2111        resp = await self._request(
2112            RequestMethod.GET,
2113            f"Forum/GetPostAndParentAwaitingApproval/{child_id}/",
2114            json={"showbanned": show_banned},
2115        )
2116        assert isinstance(resp, dict)
2117        return resp
async def fetch_topic_for_content(self, content_id: int, /) -> int:
2119    async def fetch_topic_for_content(self, content_id: int, /) -> int:
2120        resp = await self._request(
2121            RequestMethod.GET, f"Forum/GetTopicForContent/{content_id}/"
2122        )
2123        assert isinstance(resp, int)
2124        return resp
async def fetch_forum_tag_suggestions(self, partial_tag: str, /) -> dict[str, typing.Any]:
2126    async def fetch_forum_tag_suggestions(
2127        self, partial_tag: str, /
2128    ) -> typedefs.JSONObject:
2129        resp = await self._request(
2130            RequestMethod.GET,
2131            "Forum/GetForumTagSuggestions/",
2132            json={"partialtag": partial_tag},
2133        )
2134        assert isinstance(resp, dict)
2135        return resp
async def fetch_poll(self, topic_id: int, /) -> dict[str, typing.Any]:
2137    async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject:
2138        resp = await self._request(RequestMethod.GET, f"Forum/Poll/{topic_id}/")
2139        assert isinstance(resp, dict)
2140        return resp
async def fetch_recuirement_thread_summaries(self) -> list[typing.Any]:
2142    async def fetch_recuirement_thread_summaries(self) -> typedefs.JSONArray:
2143        resp = await self._request(RequestMethod.POST, "Forum/Recruit/Summaries/")
2144        assert isinstance(resp, list)
2145        return resp
async def fetch_available_avatars(self) -> collections.abc.Mapping[str, int]:
2163    async def fetch_available_avatars(self) -> collections.Mapping[str, int]:
2164        resp = await self._request(RequestMethod.GET, "GroupV2/GetAvailableAvatars/")
2165        assert isinstance(resp, dict)
2166        return resp
async def fetch_user_clan_invite_setting( self, access_token: str, /, membership_type: Union[int, aiobungie.MembershipType]) -> bool:
2168    async def fetch_user_clan_invite_setting(
2169        self,
2170        access_token: str,
2171        /,
2172        membership_type: typedefs.IntAnd[enums.MembershipType],
2173    ) -> bool:
2174        resp = await self._request(
2175            RequestMethod.GET,
2176            f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/",
2177            auth=access_token,
2178        )
2179        assert isinstance(resp, bool)
2180        return resp
async def fetch_banned_group_members( self, access_token: str, group_id: int, /, *, page: int = 1) -> dict[str, typing.Any]:
2182    async def fetch_banned_group_members(
2183        self, access_token: str, group_id: int, /, *, page: int = 1
2184    ) -> typedefs.JSONObject:
2185        resp = await self._request(
2186            RequestMethod.GET,
2187            f"GroupV2/{group_id}/Banned/?currentpage={page}",
2188            auth=access_token,
2189        )
2190        assert isinstance(resp, dict)
2191        return resp
async def fetch_pending_group_memberships( self, access_token: str, group_id: int, /, *, current_page: int = 1) -> dict[str, typing.Any]:
2193    async def fetch_pending_group_memberships(
2194        self, access_token: str, group_id: int, /, *, current_page: int = 1
2195    ) -> typedefs.JSONObject:
2196        resp = await self._request(
2197            RequestMethod.GET,
2198            f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}",
2199            auth=access_token,
2200        )
2201        assert isinstance(resp, dict)
2202        return resp
async def fetch_invited_group_memberships( self, access_token: str, group_id: int, /, *, current_page: int = 1) -> dict[str, typing.Any]:
2204    async def fetch_invited_group_memberships(
2205        self, access_token: str, group_id: int, /, *, current_page: int = 1
2206    ) -> typedefs.JSONObject:
2207        resp = await self._request(
2208            RequestMethod.GET,
2209            f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}",
2210            auth=access_token,
2211        )
2212        assert isinstance(resp, dict)
2213        return resp
async def invite_member_to_group( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], *, message: Union[aiobungie.UndefinedType, str] = UNDEFINED) -> dict[str, typing.Any]:
2215    async def invite_member_to_group(
2216        self,
2217        access_token: str,
2218        /,
2219        group_id: int,
2220        membership_id: int,
2221        membership_type: typedefs.IntAnd[enums.MembershipType],
2222        *,
2223        message: undefined.UndefinedOr[str] = undefined.UNDEFINED,
2224    ) -> typedefs.JSONObject:
2225        resp = await self._request(
2226            RequestMethod.POST,
2227            f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/",
2228            auth=access_token,
2229            json={"message": str(message)},
2230        )
2231        assert isinstance(resp, dict)
2232        return resp
async def cancel_group_member_invite( self, access_token: str, /, group_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType]) -> dict[str, typing.Any]:
2234    async def cancel_group_member_invite(
2235        self,
2236        access_token: str,
2237        /,
2238        group_id: int,
2239        membership_id: int,
2240        membership_type: typedefs.IntAnd[enums.MembershipType],
2241    ) -> typedefs.JSONObject:
2242        resp = await self._request(
2243            RequestMethod.POST,
2244            f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/",
2245            auth=access_token,
2246        )
2247        assert isinstance(resp, dict)
2248        return resp
async def fetch_historical_definition(self) -> dict[str, typing.Any]:
2250    async def fetch_historical_definition(self) -> typedefs.JSONObject:
2251        resp = await self._request(RequestMethod.GET, "Destiny2/Stats/Definition/")
2252        assert isinstance(resp, dict)
2253        return resp
async def fetch_historical_stats( self, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], day_start: datetime.datetime, day_end: datetime.datetime, groups: list[typing.Union[int, aiobungie.internal.enums.StatsGroupType]], modes: collections.abc.Sequence[typing.Union[int, aiobungie.GameMode]], *, period_type: aiobungie.internal.enums.PeriodType = <PeriodType.ALL_TIME: 2>) -> dict[str, typing.Any]:
2255    async def fetch_historical_stats(
2256        self,
2257        character_id: int,
2258        membership_id: int,
2259        membership_type: typedefs.IntAnd[enums.MembershipType],
2260        day_start: datetime.datetime,
2261        day_end: datetime.datetime,
2262        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2263        modes: collections.Sequence[typedefs.IntAnd[enums.GameMode]],
2264        *,
2265        period_type: enums.PeriodType = enums.PeriodType.ALL_TIME,
2266    ) -> typedefs.JSONObject:
2267
2268        end, start = time.parse_date_range(day_end, day_start)
2269        resp = await self._request(
2270            RequestMethod.GET,
2271            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/",
2272            json={
2273                "dayend": end,
2274                "daystart": start,
2275                "groups": [str(int(group)) for group in groups],
2276                "modes": [str(int(mode)) for mode in modes],
2277                "periodType": int(period_type),
2278            },
2279        )
2280        assert isinstance(resp, dict)
2281        return resp

Fetch historical stats for a specific membership character.

Parameters
  • character_id (int): The character ID to return the stats for.
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
  • day_start (datetime.datetime): The start of the day to return the stats for.
  • day_end (datetime.datetime): The end of the day to return the stats for.
  • groups (list[aiobungie.StatsGroupType]): A list of stats groups to return.
  • modes (list[aiobungie.GameMode | int]): A list of game modes to return.
  • period_type (aiobungie.enums.PeriodType): The period type to return the stats for. This will return ALL_TIME by default if not modified.
Returns
async def fetch_historical_stats_for_account( self, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], groups: list[typing.Union[int, aiobungie.internal.enums.StatsGroupType]]) -> dict[str, typing.Any]:
2283    async def fetch_historical_stats_for_account(
2284        self,
2285        membership_id: int,
2286        membership_type: typedefs.IntAnd[enums.MembershipType],
2287        groups: list[typedefs.IntAnd[enums.StatsGroupType]],
2288    ) -> typedefs.JSONObject:
2289        resp = await self._request(
2290            RequestMethod.GET,
2291            f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/",
2292            json={"groups": [str(int(group)) for group in groups]},
2293        )
2294        assert isinstance(resp, dict)
2295        return resp

Fetch historical stats for an account's membership.

Parameters
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
  • groups (list[aiobungie.StatsGroupType]): A list of stats groups to return.
Returns
async def fetch_aggregated_activity_stats( self, character_id: int, membership_id: int, membership_type: Union[int, aiobungie.MembershipType], /) -> dict[str, typing.Any]:
2297    async def fetch_aggregated_activity_stats(
2298        self,
2299        character_id: int,
2300        membership_id: int,
2301        membership_type: typedefs.IntAnd[enums.MembershipType],
2302        /,
2303    ) -> typedefs.JSONObject:
2304        resp = await self._request(
2305            RequestMethod.GET,
2306            f"Destiny2/{int(membership_type)}/Account/{membership_id}/"
2307            f"Character/{character_id}/Stats/AggregateActivityStats/",
2308        )
2309        assert isinstance(resp, dict)
2310        return resp

Fetch aggregated activity stats for a specific membership character.

Parameters
  • character_id (int): The character ID to return the stats for.
  • membership_id (int): The Destiny membership id to return the stats for.
  • membership_type (aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
Returns
class RESTPool:
231class RESTPool:
232    """Pool of `RESTClient` instances.
233
234    This allows to create multiple instances of `RESTClient`s that can be acquired
235    which share the same config and metadata.
236
237    Example
238    -------
239    ```py
240    import aiobungie
241    import asyncio
242
243    client_pool = aiobungie.RESTPool("token", client_id=1234, client_secret='secret')
244
245    # Using a context manager to acquire an instance
246    # of the pool and close the connection after finishing.
247
248    async def first() -> str:
249        async with client_pool.acquire() as client:
250            return client.build_oauth2_url()
251
252    async def second() -> None:
253        async with client_pool.acquire() as client:
254            new_tokens = await client.refresh_access_token("token")
255            client.metadata['tokens'] = new_tokens
256
257    # Client instances are independent from first and second.
258    await asyncio.gather(first(), second())
259    ```
260
261    Parameters
262    ----------
263    token : `str`
264        A valid application token from Bungie's developer portal.
265
266    Other Parameters
267    ----------------
268    max_retries : `int`
269        The max retries number to retry if the request hit a `5xx` status code.
270    client_secret : `typing.Optional[str]`
271        An optional application client secret,
272        This is only needed if you're fetching OAuth2 tokens with this client.
273    client_id : `typing.Optional[int]`
274        An optional application client id,
275        This is only needed if you're fetching OAuth2 tokens with this client.
276    enable_debugging : `bool | str`
277        Whether to enable logging responses or not.
278
279    Logging Levels
280    --------------
281    * `False`: This will disable logging.
282    * `True`: This will set the level to `DEBUG` and enable logging minimal information.
283    Like the response status, route, taken time and so on.
284    * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information.
285    """
286
287    __slots__ = (
288        "_token",
289        "_max_retries",
290        "_client_secret",
291        "_client_id",
292        "_metadata",
293        "_enable_debug",
294    )
295
296    # Looks like mypy doesn't like this.
297    if typing.TYPE_CHECKING:
298        _enable_debug: typing.Union[typing.Literal["TRACE"], bool, int]
299
300    def __init__(
301        self,
302        token: str,
303        /,
304        client_secret: typing.Optional[str] = None,
305        client_id: typing.Optional[int] = None,
306        *,
307        max_retries: int = 4,
308        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
309    ) -> None:
310        self._client_secret = client_secret
311        self._client_id = client_id
312        self._token: str = token
313        self._max_retries = max_retries
314        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
315        self._enable_debug = enable_debugging
316
317    @property
318    def client_id(self) -> typing.Optional[int]:
319        return self._client_id
320
321    @property
322    def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]:
323        """Pool's Metadata. This is different from client instance metadata."""
324        return self._metadata
325
326    @typing.final
327    def acquire(self) -> RESTClient:
328        """Acquires a new `RESTClient` instance from this REST pool.
329
330        Returns
331        -------
332        `RESTClient`
333            An instance of a REST client.
334        """
335        instance = RESTClient(
336            self._token,
337            client_secret=self._client_secret,
338            client_id=self._client_id,
339            max_retries=self._max_retries,
340            enable_debugging=self._enable_debug,
341        )
342        return instance

Pool of RESTClient instances.

This allows to create multiple instances of RESTClients that can be acquired which share the same config and metadata.

Example
import aiobungie
import asyncio

client_pool = aiobungie.RESTPool("token", client_id=1234, client_secret='secret')

# Using a context manager to acquire an instance
# of the pool and close the connection after finishing.

async def first() -> str:
    async with client_pool.acquire() as client:
        return client.build_oauth2_url()

async def second() -> None:
    async with client_pool.acquire() as client:
        new_tokens = await client.refresh_access_token("token")
        client.metadata['tokens'] = new_tokens

# Client instances are independent from first and second.
await asyncio.gather(first(), second())
Parameters
  • token (str): A valid application token from Bungie's developer portal.
Other Parameters
  • max_retries (int): The max retries number to retry if the request hit a 5xx status code.
  • client_secret (typing.Optional[str]): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client.
  • client_id (typing.Optional[int]): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client.
  • enable_debugging (bool | str): Whether to enable logging responses or not.
Logging Levels
  • False: This will disable logging.
  • True: This will set the level to DEBUG and enable logging minimal information. Like the response status, route, taken time and so on.
  • "TRACE" | aiobungie.TRACE: This will log the response headers along with the minimal information.
RESTPool( token: str, /, client_secret: Optional[str] = None, client_id: Optional[int] = None, *, max_retries: int = 4, enable_debugging: Union[Literal['TRACE'], bool, int] = False)
300    def __init__(
301        self,
302        token: str,
303        /,
304        client_secret: typing.Optional[str] = None,
305        client_id: typing.Optional[int] = None,
306        *,
307        max_retries: int = 4,
308        enable_debugging: typing.Union[typing.Literal["TRACE"], bool, int] = False,
309    ) -> None:
310        self._client_secret = client_secret
311        self._client_id = client_id
312        self._token: str = token
313        self._max_retries = max_retries
314        self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {}
315        self._enable_debug = enable_debugging
metadata: collections.abc.MutableMapping[typing.Any, typing.Any]

Pool's Metadata. This is different from client instance metadata.

@typing.final
def acquire(self) -> aiobungie.RESTClient:
326    @typing.final
327    def acquire(self) -> RESTClient:
328        """Acquires a new `RESTClient` instance from this REST pool.
329
330        Returns
331        -------
332        `RESTClient`
333            An instance of a REST client.
334        """
335        instance = RESTClient(
336            self._token,
337            client_secret=self._client_secret,
338            client_id=self._client_id,
339            max_retries=self._max_retries,
340            enable_debugging=self._enable_debug,
341        )
342        return instance

Acquires a new RESTClient instance from this REST pool.

Returns
@typing.final
class Race(builtins.int, aiobungie.Enum):
493@typing.final
494class Race(int, Enum):
495    """An Enum for Destiny races."""
496
497    HUMAN = 0
498    AWOKEN = 1
499    EXO = 2
500    UNKNOWN = 3

An Enum for Destiny races.

HUMAN = <Race.HUMAN: 0>
AWOKEN = <Race.AWOKEN: 1>
EXO = <Race.EXO: 2>
UNKNOWN = <Race.UNKNOWN: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Raid(builtins.int, aiobungie.Enum):
143@typing.final
144class Raid(int, Enum):
145    """An Enum for all available raids in Destiny 2."""
146
147    DSC = 910380154
148    """Deep Stone Crypt"""
149
150    LW = 2122313384
151    """Last Wish"""
152
153    VOG = 3881495763
154    """Normal Valut of Glass"""
155
156    GOS = 3458480158
157    """Garden Of Salvation"""

An Enum for all available raids in Destiny 2.

DSC = <Raid.DSC: 910380154>

Deep Stone Crypt

LW = <Raid.LW: 2122313384>

Last Wish

VOG = <Raid.VOG: 3881495763>

Normal Valut of Glass

GOS = <Raid.GOS: 3458480158>

Garden Of Salvation

Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@attrs.define(auto_exc=True)
class RateLimitedError(aiobungie.HTTPError):
253@attrs.define(auto_exc=True)
254class RateLimitedError(HTTPError):
255    """Raised when too many request status code is returned."""
256
257    http_status: http.HTTPStatus = attrs.field(
258        default=http.HTTPStatus.TOO_MANY_REQUESTS, init=False
259    )
260    """The request response http status."""
261
262    url: typedefs.StrOrURL
263    """The URL/endpoint caused this error."""
264
265    body: typing.Any
266    """The response body."""
267
268    retry_after: float = attrs.field(default=0.0)
269    """The amount of seconds you need to wait before retrying to requests."""
270
271    message: str = attrs.field(init=False)
272    """A Bungie human readable message describes the cause of the error."""
273
274    @message.default  # type: ignore
275    def _(self) -> str:
276        return f"You're ratelimited for {self.retry_after}, Endpoint: {self.url}. Slow down!"
277
278    def __str__(self) -> str:
279        return self.message

Raised when too many request status code is returned.

RateLimitedError(url: Union[str, yarl.URL], body: Any, retry_after: float = 0.0)
2def __init__(self, url, body, retry_after=attr_dict['retry_after'].default):
3    self.http_status = attr_dict['http_status'].default
4    self.url = url
5    self.body = body
6    self.retry_after = retry_after
7    self.message = __attr_factory_message(self)
8    BaseException.__init__(self, self.url,self.body,self.retry_after)

Method generated by attrs for class RateLimitedError.

http_status: http.HTTPStatus

The request response http status.

url: Union[str, yarl.URL]

The URL/endpoint caused this error.

body: Any

The response body.

retry_after: float

The amount of seconds you need to wait before retrying to requests.

message: str

A Bungie human readable message describes the cause of the error.

Inherited Members
builtins.BaseException
with_traceback
@typing.final
class RecordState(aiobungie.Flag):
48@typing.final
49class RecordState(enums.Flag):
50    """An enum for records component states."""
51
52    NONE = 0
53    REDEEMED = 1 << 0
54    UNAVAILABLE = 1 << 1
55    OBJECTIVE_NOT_COMPLETED = 1 << 2
56    OBSCURED = 1 << 3
57    INVISIBLE = 1 << 4
58    ENTITLEMENT_UNOWNED = 1 << 5
59    CAN_EQUIP_TITLE = 1 << 6

An enum for records component states.

NONE = <RecordState.NONE: 0>
REDEEMED = <RecordState.REDEEMED: 1>
UNAVAILABLE = <RecordState.UNAVAILABLE: 2>
OBJECTIVE_NOT_COMPLETED = <RecordState.OBJECTIVE_NOT_COMPLETED: 4>
OBSCURED = <RecordState.OBSCURED: 8>
INVISIBLE = <RecordState.INVISIBLE: 16>
ENTITLEMENT_UNOWNED = <RecordState.ENTITLEMENT_UNOWNED: 32>
CAN_EQUIP_TITLE = <RecordState.CAN_EQUIP_TITLE: 64>
Inherited Members
Flag
name
value
@typing.final
class Relationship(builtins.int, aiobungie.Enum):
688@typing.final
689class Relationship(int, Enum):
690    """An enum for bungie friends relationship types."""
691
692    UNKNOWN = 0
693    FRIEND = 1
694    INCOMING_REQUEST = 2
695    OUTGOING_REQUEST = 3

An enum for bungie friends relationship types.

UNKNOWN = <Relationship.UNKNOWN: 0>
FRIEND = <Relationship.FRIEND: 1>
INCOMING_REQUEST = <Relationship.INCOMING_REQUEST: 2>
OUTGOING_REQUEST = <Relationship.OUTGOING_REQUEST: 3>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class RequestMethod(builtins.str, aiobungie.Enum):
216class RequestMethod(str, enums.Enum):
217    """HTTP request methods enum."""
218
219    GET = "GET"
220    """GET methods."""
221    POST = "POST"
222    """POST methods."""
223    PUT = "PUT"
224    """PUT methods."""
225    PATCH = "PATCH"
226    """PATCH methods."""
227    DELETE = "DELETE"
228    """DELETE methods"""

HTTP request methods enum.

GET = <RequestMethod.GET: GET>

GET methods.

POST = <RequestMethod.POST: POST>

POST methods.

PUT = <RequestMethod.PUT: PUT>

PUT methods.

PATCH = <RequestMethod.PATCH: PATCH>

PATCH methods.

DELETE = <RequestMethod.DELETE: DELETE>

DELETE methods

Inherited Members
Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
@attrs.define(auto_exc=True)
class ResponseError(aiobungie.HTTPException):
248@attrs.define(auto_exc=True)
249class ResponseError(HTTPException):
250    """Exception for other HTTP response errors."""

Exception for other HTTP response errors.

ResponseError( *, error_code: int, http_status: http.HTTPStatus, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.http_status = http_status
 5    self.throttle_seconds = throttle_seconds
 6    self.url = url
 7    self.body = body
 8    self.headers = headers
 9    self.message = message
10    self.error_status = error_status
11    self.message_data = message_data
12    BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class ResponseError.

Inherited Members
HTTPException
error_code
http_status
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
@typing.final
class Stat(builtins.int, aiobungie.Enum):
515@typing.final
516class Stat(int, Enum):
517    """An Enum for Destiny 2 character stats."""
518
519    NONE = 0
520    MOBILITY = 2996146975
521    RESILIENCE = 392767087
522    RECOVERY = 1943323491
523    DISCIPLINE = 1735777505
524    INTELLECT = 144602215
525    STRENGTH = 4244567218
526    LIGHT_POWER = 1935470627

An Enum for Destiny 2 character stats.

NONE = <Stat.NONE: 0>
MOBILITY = <Stat.MOBILITY: 2996146975>
RESILIENCE = <Stat.RESILIENCE: 392767087>
RECOVERY = <Stat.RECOVERY: 1943323491>
DISCIPLINE = <Stat.DISCIPLINE: 1735777505>
INTELLECT = <Stat.INTELLECT: 144602215>
STRENGTH = <Stat.STRENGTH: 4244567218>
LIGHT_POWER = <Stat.LIGHT_POWER: 1935470627>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
TRACE = 5
@typing.final
class TierType(builtins.int, aiobungie.Enum):
630@typing.final
631class TierType(int, Enum):
632    """An enum for a Destiny 2 item tier type."""
633
634    UNKNOWN = 0
635    CURRENCY = 1
636    BASIC = 2
637    COMMON = 3
638    RARE = 4
639    SUPERIOR = 5
640    EXOTIC = 6

An enum for a Destiny 2 item tier type.

UNKNOWN = <TierType.UNKNOWN: 0>
CURRENCY = <TierType.CURRENCY: 1>
BASIC = <TierType.BASIC: 2>
COMMON = <TierType.COMMON: 3>
RARE = <TierType.RARE: 4>
SUPERIOR = <TierType.SUPERIOR: 5>
EXOTIC = <TierType.EXOTIC: 6>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class TransferStatus(aiobungie.Flag):
740@typing.final
741class TransferStatus(Flag):
742    """An enum for items transfer statuses."""
743
744    CAN_TRANSFER = 0
745    """The item can be transferred."""
746    IS_EQUIPPED = 1 << 0
747    """You can't transfer since the item is equipped."""
748    NOT_TRASNFERRABLE = 1 << 1
749    """This item can not be transferred."""
750    COULD_BE_TRANSFERRED = 1 << 2
751    """You can trasnfer the item. But the place you're trying to put it at has no space for it."""

An enum for items transfer statuses.

CAN_TRANSFER = <TransferStatus.CAN_TRANSFER: 0>

The item can be transferred.

IS_EQUIPPED = <TransferStatus.IS_EQUIPPED: 1>

You can't transfer since the item is equipped.

NOT_TRASNFERRABLE = <TransferStatus.NOT_TRASNFERRABLE: 2>

This item can not be transferred.

COULD_BE_TRANSFERRED = <TransferStatus.COULD_BE_TRANSFERRED: 4>

You can trasnfer the item. But the place you're trying to put it at has no space for it.

Inherited Members
Flag
name
value
UNDEFINED = UNDEFINED
@attrs.define(auto_exc=True)
class Unauthorized(aiobungie.HTTPException):
155@attrs.define(auto_exc=True)
156class Unauthorized(HTTPException):
157    """An exception that's raised when trying to make unauthorized call to a resource and it returns 404."""
158
159    http_status: http.HTTPStatus = attrs.field(
160        default=http.HTTPStatus.UNAUTHORIZED, init=False
161    )

An exception that's raised when trying to make unauthorized call to a resource and it returns 404.

Unauthorized( *, error_code: int, throttle_seconds: int, url: Union[str, yarl.URL, NoneType], body: Any, headers: multidict._multidict.CIMultiDictProxy[str], message: str, error_status: str, message_data: dict[str, str])
 2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data):
 3    self.error_code = error_code
 4    self.throttle_seconds = throttle_seconds
 5    self.url = url
 6    self.body = body
 7    self.headers = headers
 8    self.message = message
 9    self.error_status = error_status
10    self.message_data = message_data
11    self.http_status = attr_dict['http_status'].default
12    BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)

Method generated by attrs for class Unauthorized.

http_status: http.HTTPStatus

The request response http status.

Inherited Members
HTTPException
error_code
throttle_seconds
url
body
headers
message
error_status
message_data
builtins.BaseException
with_traceback
UndefinedOr = typing.Union[aiobungie.UndefinedType, +_T]
class UndefinedType:
33class UndefinedType:
34    """An `UNDEFINED` type."""
35
36    __instance: typing.Optional[UndefinedType] = None
37
38    def __bool__(self) -> typing.Literal[False]:
39        return False
40
41    def __int__(self) -> typing.Literal[0]:
42        return 0
43
44    def __repr__(self) -> str:
45        return "UNDEFINED"
46
47    def __str__(self) -> str:
48        return "UNDEFINED"
49
50    def __new__(cls) -> UndefinedType:
51        if cls.__instance is None:
52            o = super().__new__(cls)
53            cls.__instance = o
54        return cls.__instance

An UNDEFINED type.

UndefinedType()
@typing.final
class ValueUIStyle(builtins.int, aiobungie.Enum):
75@typing.final
76class ValueUIStyle(int, enums.Enum):
77    AUTOMATIC = 0
78    FRACTION = 1
79    CHECK_BOX = 2
80    PERCENTAGE = 3
81    DATETIME = 4
82    FRACTION_FLOAT = 5
83    INTEGER = 6
84    TIME_DURATION = 7
85    HIDDEN = 8
86    MULTIPLIER = 9
87    GREEN_PIPS = 10
88    RED_PIPS = 11
89    EXPLICIT_PERCENTAGE = 12
90    RAW_FLOAT = 13
91    LEVEL_AND_REWARD = 14

An enumeration.

AUTOMATIC = <ValueUIStyle.AUTOMATIC: 0>
FRACTION = <ValueUIStyle.FRACTION: 1>
CHECK_BOX = <ValueUIStyle.CHECK_BOX: 2>
PERCENTAGE = <ValueUIStyle.PERCENTAGE: 3>
DATETIME = <ValueUIStyle.DATETIME: 4>
FRACTION_FLOAT = <ValueUIStyle.FRACTION_FLOAT: 5>
INTEGER = <ValueUIStyle.INTEGER: 6>
TIME_DURATION = <ValueUIStyle.TIME_DURATION: 7>
HIDDEN = <ValueUIStyle.HIDDEN: 8>
MULTIPLIER = <ValueUIStyle.MULTIPLIER: 9>
GREEN_PIPS = <ValueUIStyle.GREEN_PIPS: 10>
RED_PIPS = <ValueUIStyle.RED_PIPS: 11>
EXPLICIT_PERCENTAGE = <ValueUIStyle.EXPLICIT_PERCENTAGE: 12>
RAW_FLOAT = <ValueUIStyle.RAW_FLOAT: 13>
LEVEL_AND_REWARD = <ValueUIStyle.LEVEL_AND_REWARD: 14>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class Vendor(builtins.int, aiobungie.Enum):
240@typing.final
241class Vendor(int, Enum):
242    """An Enum for all available vendors in Destiny 2."""
243
244    ZAVALA = 69482069
245    XUR = 2190858386
246    BANSHE = 672118013
247    SPIDER = 863940356
248    SHAXX = 3603221665
249    KADI = 529635856
250    """Postmaster exo."""
251    YUNA = 1796504621
252    """Asia servers only."""
253    EVERVERSE = 3361454721
254    AMANDA = 460529231
255    """Amanda holiday"""
256    CROW = 3611983588
257    HAWTHORNE = 3347378076
258    ADA1 = 350061650
259    DRIFTER = 248695599
260    IKORA = 1976548992
261    SAINT = 765357505
262    """Saint-14"""
263    ERIS_MORN = 1616085565
264    SHAW_HAWN = 1816541247
265    """COSMODROME Guy"""
266    VARIKS = 2531198101

An Enum for all available vendors in Destiny 2.

ZAVALA = <Vendor.ZAVALA: 69482069>
XUR = <Vendor.XUR: 2190858386>
BANSHE = <Vendor.BANSHE: 672118013>
SPIDER = <Vendor.SPIDER: 863940356>
SHAXX = <Vendor.SHAXX: 3603221665>
KADI = <Vendor.KADI: 529635856>

Postmaster exo.

YUNA = <Vendor.YUNA: 1796504621>

Asia servers only.

EVERVERSE = <Vendor.EVERVERSE: 3361454721>
AMANDA = <Vendor.AMANDA: 460529231>

Amanda holiday

CROW = <Vendor.CROW: 3611983588>
HAWTHORNE = <Vendor.HAWTHORNE: 3347378076>
ADA1 = <Vendor.ADA1: 350061650>
DRIFTER = <Vendor.DRIFTER: 248695599>
IKORA = <Vendor.IKORA: 1976548992>
SAINT = <Vendor.SAINT: 765357505>

Saint-14

ERIS_MORN = <Vendor.ERIS_MORN: 1616085565>
SHAW_HAWN = <Vendor.SHAW_HAWN: 1816541247>

COSMODROME Guy

VARIKS = <Vendor.VARIKS: 2531198101>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@typing.final
class WeaponType(builtins.int, aiobungie.Enum):
529@typing.final
530class WeaponType(int, Enum):
531    """Enums for The three Destiny Weapon Types"""
532
533    NONE = 0
534    KINETIC = 1498876634
535    ENERGY = 2465295065
536    POWER = 953998645

Enums for The three Destiny Weapon Types

NONE = <WeaponType.NONE: 0>
KINETIC = <WeaponType.KINETIC: 1498876634>
ENERGY = <WeaponType.ENERGY: 2465295065>
POWER = <WeaponType.POWER: 953998645>
Inherited Members
Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
annotations = _Feature((3, 7, 0, 'beta', 1), (3, 11, 0, 'alpha', 0), 16777216)
def into_iter( iterable: collections.abc.Iterable[~Item]) -> aiobungie.Iterator[~Item]:
559def into_iter(
560    iterable: collections.Iterable[Item],
561) -> Iterator[Item]:
562    """Transform an iterable into an flat iterator.
563
564    Example
565    -------
566    ```py
567    sequence = [1,2,3]
568    for item in aiobungie.into_iter(sequence).reversed():
569        print(item)
570    # 3
571    # 2
572    # 1
573    ```
574
575    Parameters
576    ----------
577    iterable: `typing.Iterable[Item]`
578        The iterable to convert.
579
580    Raises
581    ------
582    `StopIteration`
583        If no elements are left in the iterator.
584    """
585    return Iterator(iterable)

Transform an iterable into an flat iterator.

Example
sequence = [1,2,3]
for item in aiobungie.into_iter(sequence).reversed():
    print(item)
# 3
# 2
# 1
Parameters
  • iterable (typing.Iterable[Item]): The iterable to convert.
Raises
  • StopIteration: If no elements are left in the iterator.
async def raise_error( response: aiohttp.client_reqrep.ClientResponse) -> aiobungie.AiobungieError:
282async def raise_error(response: aiohttp.ClientResponse) -> AiobungieError:
283    """Generates and raise exceptions on error responses."""
284
285    # Not a JSON response, raise immediately.
286
287    # Also Bungie sometimes get funky and return HTML instead of JSON when making an authorized
288    # request with a dummy access token. I can't really do anything about this..
289    if response.content_type != "application/json":
290        return HTTPError(
291            f"Expected JSON content but got {response.content_type!s}, {response.real_url!s}",
292            http.HTTPStatus.UNSUPPORTED_MEDIA_TYPE,
293        )
294
295    body = await response.json()
296    message: str = body.get("Message", "UNDEFINED_MESSAGE")
297    error_status: str = body.get("ErrorStatus", "UNDEFINED_ERROR_STATUS")
298    message_data: dict[str, str] = body.get("MessageData", {})
299    throttle_seconds: int = body.get("ThrottleSeconds", 0)
300    error_code: int = body.get("ErrorCode", 0)
301
302    # Standard HTTP status.
303    if response.status == http.HTTPStatus.NOT_FOUND:
304        return NotFound(
305            message=message,
306            error_code=error_code,
307            throttle_seconds=throttle_seconds,
308            url=str(response.real_url),
309            body=body,
310            headers=response.headers,
311            error_status=error_status,
312            message_data=message_data,
313        )
314
315    elif response.status == http.HTTPStatus.FORBIDDEN:
316        return Forbidden(
317            message=message,
318            error_code=error_code,
319            throttle_seconds=throttle_seconds,
320            url=str(response.real_url),
321            body=body,
322            headers=response.headers,
323            error_status=error_status,
324            message_data=message_data,
325        )
326
327    elif response.status == http.HTTPStatus.UNAUTHORIZED:
328        return Unauthorized(
329            message=message,
330            error_code=error_code,
331            throttle_seconds=throttle_seconds,
332            url=str(response.real_url),
333            body=body,
334            headers=response.headers,
335            error_status=error_status,
336            message_data=message_data,
337        )
338
339    elif response.status == http.HTTPStatus.BAD_REQUEST:
340        # Membership needs to be alone.
341        if error_status == "InvalidParameters":
342            return MembershipTypeError(
343                message=message,
344                body=body,
345                headers=response.headers,
346                url=str(response.url),
347                membership_type=message_data["membershipType"],
348                required_membership=message_data["membershipInfo.membershipType"],
349                membership_id=int(message_data["membershipId"]),
350            )
351        return BadRequest(
352            message=message,
353            body=body,
354            headers=response.headers,
355            url=str(response.url),
356        )
357
358    status = http.HTTPStatus(response.status)
359
360    if 400 <= status < 500:
361        return ResponseError(
362            message=message,
363            error_code=error_code,
364            throttle_seconds=throttle_seconds,
365            url=str(response.real_url),
366            body=body,
367            headers=response.headers,
368            error_status=error_status,
369            message_data=message_data,
370            http_status=status,
371        )
372
373    # Need to self handle ~5xx errors
374    elif 500 <= status < 600:
375        # No API key or method requires OAuth2 most likely.
376        if error_status in {
377            "ApiKeyMissingFromRequest",
378            "WebAuthRequired",
379            "ApiInvalidOrExpiredKey",
380            "AuthenticationInvalid",
381            "AuthorizationCodeInvalid",
382        }:
383            return Unauthorized(
384                message=message,
385                error_code=error_code,
386                throttle_seconds=throttle_seconds,
387                url=str(response.real_url),
388                body=body,
389                headers=response.headers,
390                error_status=error_status,
391                message_data=message_data,
392            )
393
394        # Anything contains not found.
395        elif (
396            "NotFound" in error_status or error_status == "UserCannotFindRequestedUser"
397        ):
398            return NotFound(
399                message=message,
400                error_code=error_code,
401                throttle_seconds=throttle_seconds,
402                url=str(response.real_url),
403                body=body,
404                headers=response.headers,
405                error_status=error_status,
406                message_data=message_data,
407            )
408
409        # Other 5xx errors.
410        else:
411            return InternalServerError(
412                message=message,
413                error_code=error_code,
414                throttle_seconds=throttle_seconds,
415                url=str(response.real_url),
416                body=body,
417                headers=response.headers,
418                error_status=error_status,
419                message_data=message_data,
420                http_status=status,
421            )
422    # Something else.
423    else:
424        return HTTPException(
425            message=message,
426            error_code=error_code,
427            throttle_seconds=throttle_seconds,
428            url=str(response.real_url),
429            body=body,
430            headers=response.headers,
431            error_status=error_status,
432            message_data=message_data,
433            http_status=status,
434        )

Generates and raise exceptions on error responses.

def stringify_http_message(headers: 'collections.Mapping[str, str]') -> str:
437def stringify_http_message(headers: collections.Mapping[str, str]) -> str:
438    return (
439        "{ \n"
440        + "\n".join(  # noqa: W503
441            f"{f'   {key}'}: {value}"
442            if key not in ("Authorization", "X-API-KEY")
443            else f"   {key}: HIDDEN_TOKEN"
444            for key, value in headers.items()
445        )
446        + "\n}"  # noqa: W503
447    )